blob: 0acfbd9a58037161207681609317652b2acf52d7 [file] [log] [blame]
/**
*
* Copyright (c) 2023 Project CHIP Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <Matter/Matter.h>
#import "MTRTestCase+ServerAppRunner.h"
#import "MTRTestCase.h"
#import "MTRTestKeys.h"
#import "MTRTestStorage.h"
// Fixture 1: chip-all-clusters-app --KVS "$(mktemp -t chip-test-kvs)" --interface-id -1
static const uint16_t kLocalPort = 5541;
static const uint16_t kTestVendorId = 0xFFF1u;
static const __auto_type kTestProductIds = @[ @(0x8000u), @(0x8001u) ];
static const __auto_type kTestDiscriminators = @[ @(2000), @(3839u), @(3840u) ];
static const uint16_t kDiscoverDeviceTimeoutInSeconds = 10;
static const uint16_t kExpectedDiscoveredDevicesCount = 3;
// Singleton controller we use.
static MTRDeviceController * sController = nil;
static NSString * kInstanceNameKey = @"instanceName";
static NSString * kVendorIDKey = @"vendorID";
static NSString * kProductIDKey = @"productID";
static NSString * kDiscriminatorKey = @"discriminator";
static NSString * kCommissioningModeKey = @"commissioningMode";
static NSDictionary<NSString *, id> * ResultSnapshot(MTRCommissionableBrowserResult * result)
{
return @{
kInstanceNameKey : result.instanceName,
kVendorIDKey : result.vendorID,
kProductIDKey : result.productID,
kDiscriminatorKey : result.discriminator,
kCommissioningModeKey : @(result.commissioningMode),
};
}
@interface DeviceScannerDelegate : NSObject <MTRCommissionableBrowserDelegate>
@property (nonatomic, nullable) XCTestExpectation * expectation;
@property (nonatomic) NSMutableArray<NSDictionary<NSString *, id> *> * results;
@property (nonatomic) NSMutableArray<NSDictionary<NSString *, id> *> * removedResults;
@property (nonatomic) BOOL expectedResultsCountReached;
- (instancetype)initWithExpectation:(XCTestExpectation *)expectation;
- (void)controller:(MTRDeviceController *)controller didFindCommissionableDevice:(MTRCommissionableBrowserResult *)device;
- (void)controller:(MTRDeviceController *)controller didRemoveCommissionableDevice:(MTRCommissionableBrowserResult *)device;
@end
@implementation DeviceScannerDelegate
- (instancetype)initWithExpectation:(XCTestExpectation *)expectation
{
if (!(self = [super init])) {
return nil;
}
_expectation = expectation;
_results = [[NSMutableArray alloc] init];
_removedResults = [[NSMutableArray alloc] init];
_expectedResultsCountReached = NO;
return self;
}
- (instancetype)init
{
if (!(self = [super init])) {
return nil;
}
_expectation = nil;
_results = [[NSMutableArray alloc] init];
_removedResults = [[NSMutableArray alloc] init];
_expectedResultsCountReached = NO;
return self;
}
- (void)controller:(MTRDeviceController *)controller didFindCommissionableDevice:(MTRCommissionableBrowserResult *)device
{
if (self.expectation == nil) {
// We are not actually supposed to be looking at results; don't do it,
// because we may be starting/stopping browse multiple times and seeing
// odd numbers of results.
return;
}
__auto_type * snapshot = ResultSnapshot(device);
XCTAssertFalse([_results containsObject:snapshot], @"Newly discovered device %@ should not be in results already.", snapshot);
[_results addObject:snapshot];
if (_results.count == kExpectedDiscoveredDevicesCount) {
// Do some sanity checking on our results and removedResults to make
// sure we really only saw the relevant set of things.
NSSet<NSDictionary<NSString *, id> *> * finalResultsSet = [NSSet setWithArray:_results];
NSSet<NSDictionary<NSString *, id> *> * allResultsSet = [finalResultsSet copy];
allResultsSet = [allResultsSet setByAddingObjectsFromArray:_removedResults];
// Ensure that we just saw the same results as our final set popping in and out if things
// ever got removed here.
XCTAssertEqualObjects(finalResultsSet, allResultsSet);
// If we have a remove and re-add after the result count reached the
// expected one, we can end up in this branch again. Doing the above
// checks is fine, but we shouldn't double-fulfill the expectation.
if (self.expectedResultsCountReached == NO) {
self.expectedResultsCountReached = YES;
[self.expectation fulfill];
}
}
XCTAssertLessThanOrEqual(_results.count, kExpectedDiscoveredDevicesCount);
__auto_type instanceName = device.instanceName;
__auto_type vendorId = device.vendorID;
__auto_type productId = device.productID;
__auto_type discriminator = device.discriminator;
__auto_type commissioningMode = device.commissioningMode;
XCTAssertEqual(instanceName.length, 16); // The instance name is random, so just ensure the len is right.
XCTAssertEqualObjects(vendorId, @(kTestVendorId));
XCTAssertTrue([kTestProductIds containsObject:productId]);
XCTAssertTrue([kTestDiscriminators containsObject:discriminator]);
XCTAssertEqual(commissioningMode, YES);
NSLog(@"Found Device (%@) with discriminator: %@ (vendor: %@, product: %@)", instanceName, discriminator, vendorId, productId);
}
- (void)controller:(MTRDeviceController *)controller didRemoveCommissionableDevice:(MTRCommissionableBrowserResult *)device
{
__auto_type instanceName = device.instanceName;
__auto_type vendorId = device.vendorID;
__auto_type productId = device.productID;
__auto_type discriminator = device.discriminator;
NSLog(
@"Removed Device (%@) with discriminator: %@ (vendor: %@, product: %@)", instanceName, discriminator, vendorId, productId);
__auto_type * snapshot = ResultSnapshot(device);
XCTAssertTrue([_results containsObject:snapshot], @"Removed device %@ is not something we found before", snapshot);
[_removedResults addObject:snapshot];
[_results removeObject:snapshot];
}
@end
@interface MTRCommissionableBrowserTests : MTRTestCase
@end
@implementation MTRCommissionableBrowserTests
+ (void)setUp
{
[super setUp];
__auto_type * factory = [MTRDeviceControllerFactory sharedInstance];
XCTAssertNotNil(factory);
__auto_type * storage = [[MTRTestStorage alloc] init];
__auto_type * factoryParams = [[MTRDeviceControllerFactoryParams alloc] initWithStorage:storage];
factoryParams.port = @(kLocalPort);
BOOL ok = [factory startControllerFactory:factoryParams error:nil];
XCTAssertTrue(ok);
__auto_type * testKeys = [[MTRTestKeys alloc] init];
XCTAssertNotNil(testKeys);
__auto_type * params = [[MTRDeviceControllerStartupParams alloc] initWithIPK:testKeys.ipk fabricID:@(1) nocSigner:testKeys];
params.vendorID = @(kTestVendorId);
MTRDeviceController * controller = [factory createControllerOnNewFabric:params error:nil];
XCTAssertNotNil(controller);
sController = controller;
// Start the helper apps our tests use.
for (NSString * payload in @[
@"MT:Y.K90SO527JA0648G00",
@"MT:-24J0AFN00I40648G00",
]) {
BOOL started = [self startAppWithName:@"all-clusters"
arguments:@[]
payload:payload];
XCTAssertTrue(started);
}
}
+ (void)tearDown
{
MTRDeviceController * controller = sController;
XCTAssertNotNil(controller);
[controller shutdown];
XCTAssertFalse([controller isRunning]);
[[MTRDeviceControllerFactory sharedInstance] stopControllerFactory];
[super tearDown];
}
- (void)setUp
{
[super setUp];
[self setContinueAfterFailure:NO];
}
- (void)test001_StartBrowseAndStopBrowse
{
__auto_type delegate = [[DeviceScannerDelegate alloc] init];
dispatch_queue_t dispatchQueue = dispatch_queue_create("com.chip.discover", DISPATCH_QUEUE_SERIAL);
// Start browsing
XCTAssertTrue([sController startBrowseForCommissionables:delegate queue:dispatchQueue]);
// Stop browsing
XCTAssertTrue([sController stopBrowseForCommissionables]);
}
- (void)test002_StartBrowseAndStopBrowseMultipleTimes
{
__auto_type delegate = [[DeviceScannerDelegate alloc] init];
dispatch_queue_t dispatchQueue = dispatch_queue_create("com.chip.discover", DISPATCH_QUEUE_SERIAL);
// Start browsing
XCTAssertTrue([sController startBrowseForCommissionables:delegate queue:dispatchQueue]);
// Stop browsing
XCTAssertTrue([sController stopBrowseForCommissionables]);
// Start browsing a second time
XCTAssertTrue([sController startBrowseForCommissionables:delegate queue:dispatchQueue]);
// Stop browsing a second time
XCTAssertTrue([sController stopBrowseForCommissionables]);
}
- (void)test003_StopBrowseWhileNotBrowsing
{
// Stop browsing while there is no browse ongoing
XCTAssertFalse([sController stopBrowseForCommissionables]);
}
- (void)test004_StartBrowseWhileBrowsing
{
__auto_type delegate = [[DeviceScannerDelegate alloc] init];
dispatch_queue_t dispatchQueue = dispatch_queue_create("com.chip.discover", DISPATCH_QUEUE_SERIAL);
// Start browsing
XCTAssertTrue([sController startBrowseForCommissionables:delegate queue:dispatchQueue]);
// Start browsing a second time while a browse is ongoing
XCTAssertFalse([sController startBrowseForCommissionables:delegate queue:dispatchQueue]);
// Properly stop browsing
XCTAssertTrue([sController stopBrowseForCommissionables]);
}
- (void)test005_StartBrowseGetCommissionableOverMdns
{
__auto_type expectation = [self expectationWithDescription:@"Commissionable devices Found"];
__auto_type delegate = [[DeviceScannerDelegate alloc] initWithExpectation:expectation];
dispatch_queue_t dispatchQueue = dispatch_queue_create("com.chip.discover", DISPATCH_QUEUE_SERIAL);
// Start browsing
XCTAssertTrue([sController startBrowseForCommissionables:delegate queue:dispatchQueue]);
[self waitForExpectations:@[ expectation ] timeout:kDiscoverDeviceTimeoutInSeconds];
// Properly stop browsing
XCTAssertTrue([sController stopBrowseForCommissionables]);
}
@end