16
16
17
17
#import <Matter/Matter.h>
18
18
19
+ #import <dns_sd.h>
19
20
#import <os/lock.h>
20
21
21
22
#import "MTRDeviceControllerLocalTestStorage.h"
@@ -207,6 +208,109 @@ - (void)issueOperationalCertificateForRequest:(MTROperationalCSRInfo *)csrInfo
207
208
208
209
@end
209
210
211
+ @interface MTRPerControllerStorageTestsOperationalBrowser : NSObject
212
+
213
+ // expectedNodeID should be a 16-char uppercase hex string encoding the 64-bit
214
+ // node ID.
215
+ - (instancetype)initWithNodeID:(NSString *)expectedNodeID;
216
+ - (void)shutdown;
217
+
218
+ @property (nonatomic, readwrite) NSString * expectedNodeID;
219
+ @property (nonatomic, readwrite, nullable) XCTestExpectation * addedExpectation;
220
+ @property (nonatomic, readwrite, nullable) XCTestExpectation * removedExpectation;
221
+
222
+ - (void)onBrowse:(DNSServiceFlags)flags error:(DNSServiceErrorType)error instanceName:(const char *)instanceName;
223
+
224
+ @end
225
+
226
+ static void OnBrowse(DNSServiceRef serviceRef, DNSServiceFlags flags, uint32_t interfaceId,
227
+ DNSServiceErrorType error, const char * name, const char * type, const char * domain, void * context)
228
+ {
229
+ __auto_type * self = (__bridge MTRPerControllerStorageTestsOperationalBrowser *) context;
230
+ [self onBrowse:flags error:error instanceName:name];
231
+ }
232
+
233
+ @implementation MTRPerControllerStorageTestsOperationalBrowser {
234
+ DNSServiceRef _browseRef;
235
+ }
236
+
237
+ - (instancetype)initWithNodeID:(NSString *)expectedNodeID
238
+ {
239
+ if (!(self = [super init])) {
240
+ return nil;
241
+ }
242
+
243
+ _expectedNodeID = expectedNodeID;
244
+
245
+ __auto_type queue = dispatch_get_main_queue();
246
+
247
+ __auto_type err = DNSServiceBrowse(&_browseRef, /* DNSServiceFlags = */ 0, kDNSServiceInterfaceIndexAny, "_matter._tcp", "local.", OnBrowse, (__bridge void *) self);
248
+ XCTAssertEqual(err, kDNSServiceErr_NoError);
249
+ if (err != kDNSServiceErr_NoError) {
250
+ return nil;
251
+ }
252
+
253
+ err = DNSServiceSetDispatchQueue(_browseRef, queue);
254
+ XCTAssertEqual(err, kDNSServiceErr_NoError);
255
+ if (err != kDNSServiceErr_NoError) {
256
+ DNSServiceRefDeallocate(_browseRef);
257
+ _browseRef = nil;
258
+ return nil;
259
+ }
260
+
261
+ return self;
262
+ }
263
+
264
+ - (void)shutdown
265
+ {
266
+ if (_browseRef) {
267
+ DNSServiceRefDeallocate(_browseRef);
268
+ _browseRef = nil;
269
+ }
270
+ }
271
+
272
+ - (void)onBrowse:(DNSServiceFlags)flags error:(DNSServiceErrorType)error instanceName:(const char *)instanceName
273
+ {
274
+ XCTAssertEqual(error, kDNSServiceErr_NoError);
275
+ if (error != kDNSServiceErr_NoError) {
276
+ DNSServiceRefDeallocate(_browseRef);
277
+ _browseRef = nil;
278
+ return;
279
+ }
280
+
281
+ __auto_type len = strlen(instanceName);
282
+ XCTAssertEqual(len, 33); // Matter instance names are 33 chars.
283
+
284
+ if (len != 33) {
285
+ return;
286
+ }
287
+
288
+ // Skip over compressed fabric id and dash.
289
+ // TODO: Consider checking the compressed fabric ID? That's a bit hard to
290
+ // do, in general, since it depends on our keys.
291
+ const char * nodeID = &instanceName[17];
292
+ NSString * browsedNode = [NSString stringWithUTF8String:nodeID];
293
+ if (![browsedNode isEqual:self.expectedNodeID]) {
294
+ return;
295
+ }
296
+
297
+ if (flags & kDNSServiceFlagsAdd) {
298
+ if (self.addedExpectation) {
299
+ XCTestExpectation * expectation = self.addedExpectation;
300
+ self.addedExpectation = nil;
301
+ [expectation fulfill];
302
+ }
303
+ } else {
304
+ if (self.removedExpectation) {
305
+ XCTestExpectation * expectation = self.removedExpectation;
306
+ self.removedExpectation = nil;
307
+ [expectation fulfill];
308
+ }
309
+ }
310
+ }
311
+
312
+ @end
313
+
210
314
@interface MTRPerControllerStorageTests : MTRTestCase
211
315
@end
212
316
@@ -1665,6 +1769,12 @@ - (void)test011_TestDataStoreMTRDeviceWithStorageBehaviorOptimizationDisabled
1665
1769
// suspension tests to a different file.
1666
1770
- (void)test012_startSuspended
1667
1771
{
1772
+ // Needs to match the 888 == 0x378 for the node ID below.
1773
+ __auto_type * operationalBrowser = [[MTRPerControllerStorageTestsOperationalBrowser alloc] initWithNodeID:@"0000000000000378"];
1774
+ XCTestExpectation * initialAdvertisingExpectation = [self expectationWithDescription:@"Controller advertising initially"];
1775
+ operationalBrowser.addedExpectation = initialAdvertisingExpectation;
1776
+ initialAdvertisingExpectation.inverted = YES; // We should not in fact advertise, since we are suspended.
1777
+
1668
1778
NSError * error;
1669
1779
__auto_type * storageDelegate = [[MTRTestPerControllerStorage alloc] initWithControllerID:[NSUUID UUID]];
1670
1780
__auto_type * controller = [self startControllerWithRootKeys:[[MTRTestKeys alloc] init]
@@ -1675,6 +1785,7 @@ - (void)test012_startSuspended
1675
1785
caseAuthenticatedTags:nil
1676
1786
paramsModifier:^(MTRDeviceControllerExternalCertificateParameters * params) {
1677
1787
params.startSuspended = YES;
1788
+ params.shouldAdvertiseOperational = YES;
1678
1789
}
1679
1790
error:&error];
1680
1791
@@ -1691,17 +1802,26 @@ - (void)test012_startSuspended
1691
1802
[controller setupCommissioningSessionWithPayload:payload newNodeID:@(17) error:&error];
1692
1803
XCTAssertNotNil(error);
1693
1804
1805
+ [self waitForExpectations:@[ initialAdvertisingExpectation ] timeout:kTimeoutInSeconds];
1806
+
1694
1807
[controller shutdown];
1695
1808
}
1696
1809
1697
1810
- (void)test013_suspendDevices
1698
1811
{
1812
+ // getMTRDevice uses "123" for the node ID of the controller, which is hex 0x7B
1813
+ __auto_type * operationalBrowser = [[MTRPerControllerStorageTestsOperationalBrowser alloc] initWithNodeID:@"000000000000007B"];
1814
+ XCTestExpectation * initialAdvertisingExpectation = [self expectationWithDescription:@"Controller advertising initially"];
1815
+ operationalBrowser.addedExpectation = initialAdvertisingExpectation;
1816
+
1699
1817
NSNumber * deviceID = @(17);
1700
1818
__auto_type * device = [self getMTRDevice:deviceID];
1701
1819
__auto_type * controller = device.deviceController;
1702
1820
1703
1821
XCTAssertFalse(controller.suspended);
1704
1822
1823
+ [self waitForExpectations:@[ initialAdvertisingExpectation ] timeout:kTimeoutInSeconds];
1824
+
1705
1825
__auto_type queue = dispatch_get_main_queue();
1706
1826
__auto_type * delegate = [[MTRDeviceTestDelegate alloc] init];
1707
1827
@@ -1757,6 +1877,9 @@ - (void)test013_suspendDevices
1757
1877
}
1758
1878
});
1759
1879
1880
+ XCTestExpectation * advertisingStoppedExpectation = [self expectationWithDescription:@"Controller stopped advertising"];
1881
+ operationalBrowser.removedExpectation = advertisingStoppedExpectation;
1882
+
1760
1883
[controller suspend];
1761
1884
XCTAssertTrue(controller.suspended);
1762
1885
@@ -1767,7 +1890,7 @@ - (void)test013_suspendDevices
1767
1890
[toggle2Expectation fulfill];
1768
1891
}];
1769
1892
1770
- [self waitForExpectations:@[ becameUnreachableExpectation, toggle2Expectation, suspendedExpectation, browseStoppedExpectation ] timeout:kTimeoutInSeconds];
1893
+ [self waitForExpectations:@[ becameUnreachableExpectation, toggle2Expectation, suspendedExpectation, browseStoppedExpectation, advertisingStoppedExpectation ] timeout:kTimeoutInSeconds];
1771
1894
1772
1895
XCTestExpectation * newSubscriptionExpectation = [self expectationWithDescription:@"Subscription has been set up again"];
1773
1896
XCTestExpectation * newReachableExpectation = [self expectationWithDescription:@"Device became reachable again"];
@@ -1790,10 +1913,13 @@ - (void)test013_suspendDevices
1790
1913
}
1791
1914
});
1792
1915
1916
+ XCTestExpectation * advertisingResumedExpectation = [self expectationWithDescription:@"Controller resumed advertising"];
1917
+ operationalBrowser.addedExpectation = advertisingResumedExpectation;
1918
+
1793
1919
[controller resume];
1794
1920
XCTAssertFalse(controller.suspended);
1795
1921
1796
- [self waitForExpectations:@[ newSubscriptionExpectation, newReachableExpectation, resumedExpectation, browseRestartedExpectation ] timeout:kSubscriptionTimeoutInSeconds];
1922
+ [self waitForExpectations:@[ newSubscriptionExpectation, newReachableExpectation, resumedExpectation, browseRestartedExpectation, advertisingResumedExpectation ] timeout:kSubscriptionTimeoutInSeconds];
1797
1923
1798
1924
MTRSetLogCallback(MTRLogTypeProgress, nil);
1799
1925
@@ -1814,6 +1940,8 @@ - (void)test013_suspendDevices
1814
1940
ResetCommissionee(baseDevice, queue, self, kTimeoutInSeconds);
1815
1941
1816
1942
[controller shutdown];
1943
+
1944
+ [operationalBrowser shutdown];
1817
1945
}
1818
1946
1819
1947
// TODO: This might want to go in a separate test file, with some shared setup
0 commit comments