From 036c188f248e220d64e25bf4b385467a8ebc637c Mon Sep 17 00:00:00 2001 From: Jeff Tung <100387939+jtung-apple@users.noreply.github.com> Date: Fri, 6 Sep 2024 11:20:00 -0700 Subject: [PATCH] [Darwin] Add internal method to get report of all attributes to MTRDevice --- src/darwin/Framework/CHIP/MTRDevice.mm | 11 ++++ .../Framework/CHIP/MTRDevice_Concrete.mm | 31 +++++++++- .../Framework/CHIP/MTRDevice_Internal.h | 3 + .../Framework/CHIPTests/MTRDeviceTests.m | 56 +++++++++++++++++++ 4 files changed, 99 insertions(+), 2 deletions(-) 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..5c2a8b3626e62d 100644 --- a/src/darwin/Framework/CHIP/MTRDevice_Concrete.mm +++ b/src/darwin/Framework/CHIP/MTRDevice_Concrete.mm @@ -2626,7 +2626,7 @@ static BOOL AttributeHasChangesOmittedQuality(MTRAttributePath * attributePath) } // Return current known / expected value right away - NSDictionary * attributeValueToReturn = [self _attributeValueDictionaryForAttributePath:attributePath]; + NSDictionary * attributeValueToReturn = [self _getAttributeValueDictionaryForAttributePath:attributePath]; // Send read request to device if any of the following are true: // 1. Subscription not in a state we can expect reports @@ -3089,9 +3089,15 @@ - (void)_performScheduledExpirationCheck } // Get attribute value dictionary for an attribute path from the right cache -- (NSDictionary *)_attributeValueDictionaryForAttributePath:(MTRAttributePath *)attributePath +- (NSDictionary *)_getAttributeValueDictionaryForAttributePath:(MTRAttributePath *)attributePath { std::lock_guard lock(_lock); + return [self _attributeValueDictionaryForAttributePath:attributePath]; +} + +- (NSDictionary *)_attributeValueDictionaryForAttributePath:(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 _attributeValueDictionaryForAttributePath because it takes into consideration expected values too + [attributeReport addObject:[self _attributeValueDictionaryForAttributePath: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..a154e9e15e92e8 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