[Darwin] Add internal method to get report of all attributes to MTRDevice (#35463)
diff --git a/src/darwin/Framework/CHIP/MTRDevice.mm b/src/darwin/Framework/CHIP/MTRDevice.mm
index 01b6b2e..ceca6d2 100644
--- a/src/darwin/Framework/CHIP/MTRDevice.mm
+++ b/src/darwin/Framework/CHIP/MTRDevice.mm
@@ -1459,6 +1459,17 @@
}
}
+- (NSArray<NSDictionary<NSString *, id> *> *)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 70824dd..52acb7c 100644
--- a/src/darwin/Framework/CHIP/MTRDevice_Concrete.mm
+++ b/src/darwin/Framework/CHIP/MTRDevice_Concrete.mm
@@ -3092,6 +3092,12 @@
- (NSDictionary<NSString *, id> *)_attributeValueDictionaryForAttributePath:(MTRAttributePath *)attributePath
{
std::lock_guard lock(_lock);
+ return [self _lockedAttributeValueDictionaryForAttributePath:attributePath];
+}
+
+- (NSDictionary<NSString *, id> *)_lockedAttributeValueDictionaryForAttributePath:(MTRAttributePath *)attributePath
+{
+ os_unfair_lock_assert_owner(&self->_lock);
// First check expected value cache
NSArray * expectedValue = _expectedValueCache[attributePath];
@@ -3478,6 +3484,27 @@
return attributesToReport;
}
+- (NSArray<NSDictionary<NSString *, id> *> *)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 c3b6b60..9627d4f 100644
--- a/src/darwin/Framework/CHIP/MTRDevice_Internal.h
+++ b/src/darwin/Framework/CHIP/MTRDevice_Internal.h
@@ -202,6 +202,9 @@
- (void)_callFirstDelegateSynchronouslyWithBlock:(void (^)(id<MTRDeviceDelegate> delegate))block;
#endif
+// Used to generate attribute report that contains all known attributes, taking into consideration expected values
+- (NSArray<NSDictionary<NSString *, id> *> *)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 da059fc..87b7371 100644
--- a/src/darwin/Framework/CHIPTests/MTRDeviceTests.m
+++ b/src/darwin/Framework/CHIPTests/MTRDeviceTests.m
@@ -4022,6 +4022,7 @@
// 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 @@
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<NSDictionary<NSString *, id> *> * 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