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,28 @@ - (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];
1808
+
1809
+ [operationalBrowser shutdown];
1695
1810
}
1696
1811
1697
1812
- (void)test013_suspendDevices
1698
1813
{
1814
+ // getMTRDevice uses "123" for the node ID of the controller, which is hex 0x7B
1815
+ __auto_type * operationalBrowser = [[MTRPerControllerStorageTestsOperationalBrowser alloc] initWithNodeID:@"000000000000007B"];
1816
+ XCTestExpectation * initialAdvertisingExpectation = [self expectationWithDescription:@"Controller advertising initially"];
1817
+ operationalBrowser.addedExpectation = initialAdvertisingExpectation;
1818
+
1699
1819
NSNumber * deviceID = @(17);
1700
1820
__auto_type * device = [self getMTRDevice:deviceID];
1701
1821
__auto_type * controller = device.deviceController;
1702
1822
1703
1823
XCTAssertFalse(controller.suspended);
1704
1824
1825
+ [self waitForExpectations:@[ initialAdvertisingExpectation ] timeout:kTimeoutInSeconds];
1826
+
1705
1827
__auto_type queue = dispatch_get_main_queue();
1706
1828
__auto_type * delegate = [[MTRDeviceTestDelegate alloc] init];
1707
1829
@@ -1757,6 +1879,9 @@ - (void)test013_suspendDevices
1757
1879
}
1758
1880
});
1759
1881
1882
+ XCTestExpectation * advertisingStoppedExpectation = [self expectationWithDescription:@"Controller stopped advertising"];
1883
+ operationalBrowser.removedExpectation = advertisingStoppedExpectation;
1884
+
1760
1885
[controller suspend];
1761
1886
XCTAssertTrue(controller.suspended);
1762
1887
@@ -1767,7 +1892,7 @@ - (void)test013_suspendDevices
1767
1892
[toggle2Expectation fulfill];
1768
1893
}];
1769
1894
1770
- [self waitForExpectations:@[ becameUnreachableExpectation, toggle2Expectation, suspendedExpectation, browseStoppedExpectation ] timeout:kTimeoutInSeconds];
1895
+ [self waitForExpectations:@[ becameUnreachableExpectation, toggle2Expectation, suspendedExpectation, browseStoppedExpectation, advertisingStoppedExpectation ] timeout:kTimeoutInSeconds];
1771
1896
1772
1897
XCTestExpectation * newSubscriptionExpectation = [self expectationWithDescription:@"Subscription has been set up again"];
1773
1898
XCTestExpectation * newReachableExpectation = [self expectationWithDescription:@"Device became reachable again"];
@@ -1790,10 +1915,13 @@ - (void)test013_suspendDevices
1790
1915
}
1791
1916
});
1792
1917
1918
+ XCTestExpectation * advertisingResumedExpectation = [self expectationWithDescription:@"Controller resumed advertising"];
1919
+ operationalBrowser.addedExpectation = advertisingResumedExpectation;
1920
+
1793
1921
[controller resume];
1794
1922
XCTAssertFalse(controller.suspended);
1795
1923
1796
- [self waitForExpectations:@[ newSubscriptionExpectation, newReachableExpectation, resumedExpectation, browseRestartedExpectation ] timeout:kSubscriptionTimeoutInSeconds];
1924
+ [self waitForExpectations:@[ newSubscriptionExpectation, newReachableExpectation, resumedExpectation, browseRestartedExpectation, advertisingResumedExpectation ] timeout:kSubscriptionTimeoutInSeconds];
1797
1925
1798
1926
MTRSetLogCallback(MTRLogTypeProgress, nil);
1799
1927
@@ -1814,6 +1942,8 @@ - (void)test013_suspendDevices
1814
1942
ResetCommissionee(baseDevice, queue, self, kTimeoutInSeconds);
1815
1943
1816
1944
[controller shutdown];
1945
+
1946
+ [operationalBrowser shutdown];
1817
1947
}
1818
1948
1819
1949
// TODO: This might want to go in a separate test file, with some shared setup
0 commit comments