diff --git a/src/darwin/Framework/CHIP/MTRDefines_Internal.h b/src/darwin/Framework/CHIP/MTRDefines_Internal.h index e22f00dd4e6a0b..94a1cbb3da61f4 100644 --- a/src/darwin/Framework/CHIP/MTRDefines_Internal.h +++ b/src/darwin/Framework/CHIP/MTRDefines_Internal.h @@ -150,3 +150,7 @@ typedef struct {} variable_hidden_by_mtr_hide; } \ } #endif + +#ifndef YES_NO +#define YES_NO(x) ((x) ? @"YES" : @"NO") +#endif diff --git a/src/darwin/Framework/CHIP/MTRDeviceController.mm b/src/darwin/Framework/CHIP/MTRDeviceController.mm index ac9ab10c697ccb..9dc9f5a147db81 100644 --- a/src/darwin/Framework/CHIP/MTRDeviceController.mm +++ b/src/darwin/Framework/CHIP/MTRDeviceController.mm @@ -1779,6 +1779,11 @@ - (void)setDeviceControllerDelegate:(id)delegate qu { @synchronized(self) { if (_strongDelegateForSetDelegateAPI) { + if (_strongDelegateForSetDelegateAPI == delegate) { + MTR_LOG("%@ setDeviceControllerDelegate: delegate %p is already set", self, delegate); + return; + } + MTR_LOG("%@ setDeviceControllerDelegate: replacing %p with %p", self, _strongDelegateForSetDelegateAPI, delegate); [self removeDeviceControllerDelegate:_strongDelegateForSetDelegateAPI]; } @@ -1799,6 +1804,10 @@ - (void)addDeviceControllerDelegate:(id)delegate qu - (void)removeDeviceControllerDelegate:(id)delegate { @synchronized(self) { + if (_strongDelegateForSetDelegateAPI == delegate) { + _strongDelegateForSetDelegateAPI = nil; + } + __block MTRDeviceControllerDelegateInfo * delegateInfoToRemove = nil; [self _iterateDelegateInfoWithBlock:^(MTRDeviceControllerDelegateInfo * delegateInfo) { if (delegateInfo.delegate == delegate) { @@ -1865,6 +1874,13 @@ - (void)_callDelegatesWithBlock:(void (^_Nullable)(id(delegatesCalled), logString); } +#if DEBUG +- (NSUInteger)unitTestDelegateCount +{ + return [self _iterateDelegateInfoWithBlock:nil]; +} +#endif + - (void)controller:(MTRDeviceController *)controller statusUpdate:(MTRCommissioningStatus)status { [self _callDelegatesWithBlock:^(id delegate) { diff --git a/src/darwin/Framework/CHIP/MTRError_Internal.h b/src/darwin/Framework/CHIP/MTRError_Internal.h index af19f4888ff050..c79cc17d95f198 100644 --- a/src/darwin/Framework/CHIP/MTRError_Internal.h +++ b/src/darwin/Framework/CHIP/MTRError_Internal.h @@ -26,10 +26,6 @@ NS_ASSUME_NONNULL_BEGIN -#ifndef YES_NO -#define YES_NO(x) ((x) ? @"YES" : @"NO") -#endif - MTR_DIRECT_MEMBERS @interface MTRError : NSObject + (NSError *)errorWithCode:(MTRErrorCode)code; diff --git a/src/darwin/Framework/CHIPTests/MTRPairingTests.m b/src/darwin/Framework/CHIPTests/MTRPairingTests.m index cd1802dff3dd47..807e9b046e2fdc 100644 --- a/src/darwin/Framework/CHIPTests/MTRPairingTests.m +++ b/src/darwin/Framework/CHIPTests/MTRPairingTests.m @@ -18,9 +18,11 @@ // module headers #import +#import "MTRDefines_Internal.h" #import "MTRErrorTestUtils.h" #import "MTRTestCase+ServerAppRunner.h" #import "MTRTestCase.h" +#import "MTRTestDeclarations.h" #import "MTRTestKeys.h" #import "MTRTestStorage.h" @@ -131,6 +133,42 @@ - (void)controller:(MTRDeviceController *)controller commissioningComplete:(NSEr @end +@interface MTRPairingTestMonitoringControllerDelegate : NSObject +@property(nonatomic, readonly) BOOL statusUpdateCalled; +@property(nonatomic, readonly) BOOL commissioningSessionEstablishmentDoneCalled; +@property(nonatomic, readonly) BOOL commissioningCompleteCalled; +@property(nonatomic, readonly) BOOL readCommissioningInfoCalled; +@end + +@implementation MTRPairingTestMonitoringControllerDelegate +- (NSString *)description +{ + return [NSString stringWithFormat:@"", self, YES_NO(_statusUpdateCalled), YES_NO(_commissioningSessionEstablishmentDoneCalled), YES_NO(_commissioningCompleteCalled), YES_NO(_readCommissioningInfoCalled)]; +} +- (void)controller:(MTRDeviceController *)controller statusUpdate:(MTRCommissioningStatus)status +{ + _statusUpdateCalled = YES; +} + +- (void)controller:(MTRDeviceController *)controller commissioningSessionEstablishmentDone:(NSError * _Nullable)error +{ + _commissioningSessionEstablishmentDoneCalled = YES; +} + +- (void)controller:(MTRDeviceController *)controller + commissioningComplete:(NSError * _Nullable)error + nodeID:(NSNumber * _Nullable)nodeID + metrics:(MTRMetrics *)metrics +{ + _commissioningCompleteCalled = YES; +} + +- (void)controller:(MTRDeviceController *)controller readCommissioningInfo:(MTRProductIdentity *)info +{ + _readCommissioningInfoCalled = YES; +} +@end + @interface MTRPairingTests : MTRTestCase @property (nullable) MTRPairingTestControllerDelegate * controllerDelegate; @end @@ -219,6 +257,19 @@ - (void)doPairingTestWithAttestationDelegate:(id)a [sController setDeviceControllerDelegate:controllerDelegate queue:callbackQueue]; self.controllerDelegate = controllerDelegate; + // Test that a monitoring delegate works + __auto_type * monitoringControllerDelegate = [[MTRPairingTestMonitoringControllerDelegate alloc] init]; + [sController addDeviceControllerDelegate:monitoringControllerDelegate queue:callbackQueue]; + XCTAssertEqual([sController unitTestDelegateCount], 2); + + // Test that the addDeviceControllerDelegate delegate is held weakly by the controller + @autoreleasepool { + __auto_type * monitoringControllerDelegate = [[MTRPairingTestMonitoringControllerDelegate alloc] init]; + [sController addDeviceControllerDelegate:monitoringControllerDelegate queue:callbackQueue]; + XCTAssertEqual([sController unitTestDelegateCount], 3); + } + XCTAssertEqual([sController unitTestDelegateCount], 2); + NSError * error; __auto_type * payload = [MTRSetupPayload setupPayloadWithOnboardingPayload:kOnboardingPayload error:&error]; XCTAssertNotNil(payload); @@ -229,6 +280,13 @@ - (void)doPairingTestWithAttestationDelegate:(id)a [self waitForExpectations:@[ expectation ] timeout:kPairingTimeoutInSeconds]; XCTAssertNil(controllerDelegate.commissioningCompleteError); + + // Test that the monitoring delegate got all the callbacks + XCTAssertTrue(monitoringControllerDelegate.statusUpdateCalled); + XCTAssertTrue(monitoringControllerDelegate.commissioningSessionEstablishmentDoneCalled); + XCTAssertTrue(monitoringControllerDelegate.commissioningCompleteCalled); + XCTAssertTrue(monitoringControllerDelegate.readCommissioningInfoCalled); + [sController removeDeviceControllerDelegate:monitoringControllerDelegate]; } - (void)test001_PairWithoutAttestationDelegate diff --git a/src/darwin/Framework/CHIPTests/TestHelpers/MTRTestDeclarations.h b/src/darwin/Framework/CHIPTests/TestHelpers/MTRTestDeclarations.h index 46d6c61e950f2d..22998eb4496216 100644 --- a/src/darwin/Framework/CHIPTests/TestHelpers/MTRTestDeclarations.h +++ b/src/darwin/Framework/CHIPTests/TestHelpers/MTRTestDeclarations.h @@ -18,6 +18,9 @@ #import #import +// For MTRDeviceDataValueDictionary: +#import "MTRDevice_Internal.h" + NS_ASSUME_NONNULL_BEGIN #pragma mark - Declarations for internal methods @@ -55,6 +58,7 @@ NS_ASSUME_NONNULL_BEGIN #ifdef DEBUG @interface MTRDeviceController (TestDebug) - (NSDictionary *)unitTestGetDeviceAttributeCounts; +- (NSUInteger)unitTestDelegateCount; @end @interface MTRBaseDevice (TestDebug)