From b3d527e8913a7bcc50003437d377f223f144e49a Mon Sep 17 00:00:00 2001 From: Jeff Tung <100387939+jtung-apple@users.noreply.github.com> Date: Fri, 6 Sep 2024 17:27:40 -0700 Subject: [PATCH] [Darwin] Add internal method to get report of all attributes to MTRDevice (#35463) --- src/darwin/Framework/CHIP/MTRDevice.mm | 11 ++++ .../Framework/CHIP/MTRDevice_Concrete.mm | 27 +++++++++ .../Framework/CHIP/MTRDevice_Internal.h | 3 + .../Framework/CHIPTests/MTRDeviceTests.m | 56 +++++++++++++++++++ 4 files changed, 97 insertions(+) diff --git a/src/darwin/Framework/CHIP/MTRDevice.mm b/src/darwin/Framework/CHIP/MTRDevice.mm index 01b6b2ebbd24e8..ceca6d27830f8c 100644 --- a/src/darwin/Framework/CHIP/MTRDevice.mm +++ b/src/darwin/Framework/CHIP/MTRDevice.mm @@ -1459,6 +1459,17 @@ - (NSDictionary *)_dataValueWithoutDataVersion:(NSDictionary *)attributeValue } } +- (NSArray *> *)getAllAttributesReport +{ +#define MTRDeviceErrorStr "MTRDevice getAllAttributesReport must be handled by subclasses that support it" + MTR_LOG_ERROR(MTRDeviceErrorStr); +#ifdef DEBUG + NSAssert(NO, @MTRDeviceErrorStr); +#endif // DEBUG +#undef MTRDeviceErrorStr + return nil; +} + #ifdef DEBUG - (NSUInteger)unitTestAttributeCount { diff --git a/src/darwin/Framework/CHIP/MTRDevice_Concrete.mm b/src/darwin/Framework/CHIP/MTRDevice_Concrete.mm index 70824dd9bcd7d1..52acb7c0b4c09e 100644 --- a/src/darwin/Framework/CHIP/MTRDevice_Concrete.mm +++ b/src/darwin/Framework/CHIP/MTRDevice_Concrete.mm @@ -3092,6 +3092,12 @@ - (void)_performScheduledExpirationCheck - (NSDictionary *)_attributeValueDictionaryForAttributePath:(MTRAttributePath *)attributePath { std::lock_guard lock(_lock); + return [self _lockedAttributeValueDictionaryForAttributePath:attributePath]; +} + +- (NSDictionary *)_lockedAttributeValueDictionaryForAttributePath:(MTRAttributePath *)attributePath +{ + os_unfair_lock_assert_owner(&self->_lock); // First check expected value cache NSArray * expectedValue = _expectedValueCache[attributePath]; @@ -3478,6 +3484,27 @@ - (NSArray *)_getAttributesToReportWithReportedValues:(NSArray *> *)getAllAttributesReport +{ + std::lock_guard lock(_lock); + + NSMutableArray * attributeReport = [NSMutableArray array]; + for (MTRClusterPath * clusterPath in [self _knownClusters]) { + MTRDeviceClusterData * clusterData = [self _clusterDataForPath:clusterPath]; + + for (NSNumber * attributeID in clusterData.attributes) { + auto * attributePath = [MTRAttributePath attributePathWithEndpointID:clusterPath.endpoint + clusterID:clusterPath.cluster + attributeID:attributeID]; + + // Using _lockedAttributeValueDictionaryForAttributePath because it takes into consideration expected values too + [attributeReport addObject:[self _lockedAttributeValueDictionaryForAttributePath:attributePath]]; + } + } + + return attributeReport; +} + #ifdef DEBUG - (NSUInteger)unitTestAttributeCount { diff --git a/src/darwin/Framework/CHIP/MTRDevice_Internal.h b/src/darwin/Framework/CHIP/MTRDevice_Internal.h index c3b6b60fa6314e..9627d4fccf56b8 100644 --- a/src/darwin/Framework/CHIP/MTRDevice_Internal.h +++ b/src/darwin/Framework/CHIP/MTRDevice_Internal.h @@ -202,6 +202,9 @@ MTR_DIRECT_MEMBERS - (void)_callFirstDelegateSynchronouslyWithBlock:(void (^)(id delegate))block; #endif +// Used to generate attribute report that contains all known attributes, taking into consideration expected values +- (NSArray *> *)getAllAttributesReport; + @end #pragma mark - MTRDevice internal state monitoring diff --git a/src/darwin/Framework/CHIPTests/MTRDeviceTests.m b/src/darwin/Framework/CHIPTests/MTRDeviceTests.m index da059fc505de8c..87b7371ffc5fd8 100644 --- a/src/darwin/Framework/CHIPTests/MTRDeviceTests.m +++ b/src/darwin/Framework/CHIPTests/MTRDeviceTests.m @@ -4022,6 +4022,7 @@ - (void)test037_MTRDeviceMultipleDelegatesGetReports // For unit test no real data is needed, but timestamp is required }; } + - (void)test038_MTRDeviceMultipleDelegatesInterestedPaths { dispatch_queue_t queue = dispatch_get_main_queue(); @@ -4274,6 +4275,61 @@ - (void)test038_MTRDeviceMultipleDelegatesInterestedPaths XCTAssertEqual(eventsReceived4, 36); } +- (void)test039_GetAllAttributesReport +{ + dispatch_queue_t queue = dispatch_get_main_queue(); + + // First start with clean slate by removing the MTRDevice and clearing the persisted cache + __auto_type * device = [MTRDevice deviceWithNodeID:@(kDeviceId) controller:sController]; + [sController removeDevice:device]; + [sController.controllerDataStore clearAllStoredClusterData]; + NSDictionary * storedClusterDataAfterClear = [sController.controllerDataStore getStoredClusterDataForNodeID:@(kDeviceId)]; + XCTAssertEqual(storedClusterDataAfterClear.count, 0); + + // Now recreate device and get subscription primed + device = [MTRDevice deviceWithNodeID:@(kDeviceId) controller:sController]; + XCTestExpectation * gotReportEnd = [self expectationWithDescription:@"Report end for delegate"]; + + __auto_type * delegate = [[MTRDeviceTestDelegateWithSubscriptionSetupOverride alloc] init]; + delegate.skipSetupSubscription = YES; + __weak __auto_type weakdelegate = delegate; + __block NSUInteger attributesReceived = 0; + delegate.onAttributeDataReceived = ^(NSArray *> * data) { + attributesReceived += data.count; + }; + delegate.onReportEnd = ^{ + [gotReportEnd fulfill]; + __strong __auto_type strongDelegate = weakdelegate; + strongDelegate.onReportEnd = nil; + }; + + [device addDelegate:delegate queue:queue]; + + // Now inject attributes and check that each delegate gets the right set of attributes + NSMutableArray * attributeReport = [NSMutableArray array]; + // Construct 36 attributes with endpoints 1~4, clusters 11 ~ 33, and attributes 111~333 + for (int i = 1; i <= 4; i++) { + for (int j = 1; j <= 3; j++) { + for (int k = 1; k <= 3; k++) { + int endpointID = i; + int clusterID = i * 10 + j; + int attributeID = i * 100 + j * 10 + k; + int value = attributeID + 10000; + [attributeReport addObject:[self _testAttributeResponseValueWithEndpointID:@(endpointID) clusterID:@(clusterID) attributeID:@(attributeID) value:value]]; + } + } + } + [device unitTestInjectAttributeReport:attributeReport fromSubscription:YES]; + + [self waitForExpectations:@[ gotReportEnd ] timeout:60]; + + XCTAssertEqual(attributesReceived, 36); + + NSArray * allAttributesReport = [device getAllAttributesReport]; + + XCTAssertEqual(allAttributesReport.count, 36); +} + @end @interface MTRDeviceEncoderTests : XCTestCase