From 81f1c78989a7f4e09615fc0678fb5800a598a340 Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Tue, 18 Jul 2023 14:35:08 -0400 Subject: [PATCH] Add a Swift XCTest for commissioning. (#27998) * Add a Swift XCTest for commissioning. * Address review comments. Also fixes some bits that did not set the error outparam when returning nil. --- .../CHIP/MTRDeviceControllerFactory.mm | 12 ++ .../CHIPTests/MTRSwiftPairingTests.swift | 109 ++++++++++++++++++ .../CHIPTests/MatterTests-Bridging-Header.h | 7 ++ .../Matter.xcodeproj/project.pbxproj | 14 +++ 4 files changed, 142 insertions(+) create mode 100644 src/darwin/Framework/CHIPTests/MTRSwiftPairingTests.swift create mode 100644 src/darwin/Framework/CHIPTests/MatterTests-Bridging-Header.h diff --git a/src/darwin/Framework/CHIP/MTRDeviceControllerFactory.mm b/src/darwin/Framework/CHIP/MTRDeviceControllerFactory.mm index 5bd050b0df028e..053a3a5c6a5094 100644 --- a/src/darwin/Framework/CHIP/MTRDeviceControllerFactory.mm +++ b/src/darwin/Framework/CHIP/MTRDeviceControllerFactory.mm @@ -560,6 +560,9 @@ - (MTRDeviceController * _Nullable)createControllerOnExistingFabric:(MTRDeviceCo if (![self checkIsRunning:error]) { MTR_LOG_ERROR("Trying to start controller while Matter controller factory is not running"); + if (error != nil) { + *error = [MTRError errorForCHIPErrorCode:CHIP_ERROR_INCORRECT_STATE]; + } return nil; } @@ -653,16 +656,25 @@ - (MTRDeviceController * _Nullable)createControllerOnNewFabric:(MTRDeviceControl if (![self isRunning]) { MTR_LOG_ERROR("Trying to start controller while Matter controller factory is not running"); + if (error != nil) { + *error = [MTRError errorForCHIPErrorCode:CHIP_ERROR_INCORRECT_STATE]; + } return nil; } if (startupParams.vendorID == nil) { MTR_LOG_ERROR("Must provide vendor id when starting controller on new fabric"); + if (error != nil) { + *error = [MTRError errorForCHIPErrorCode:CHIP_ERROR_INVALID_ARGUMENT]; + } return nil; } if (startupParams.intermediateCertificate != nil && startupParams.rootCertificate == nil) { MTR_LOG_ERROR("Must provide a root certificate when using an intermediate certificate"); + if (error != nil) { + *error = [MTRError errorForCHIPErrorCode:CHIP_ERROR_INVALID_ARGUMENT]; + } return nil; } diff --git a/src/darwin/Framework/CHIPTests/MTRSwiftPairingTests.swift b/src/darwin/Framework/CHIPTests/MTRSwiftPairingTests.swift new file mode 100644 index 00000000000000..70c95d5c42efb4 --- /dev/null +++ b/src/darwin/Framework/CHIPTests/MTRSwiftPairingTests.swift @@ -0,0 +1,109 @@ +import Matter +import XCTest + +// This more or less parallels the "no delegate" case in MTRPairingTests + +struct Constants { + static let localPort = 5541 + static let vendorID = 0xFFF1 + static let onboardingPayload = "MT:Y.K90SO527JA0648G00" + static let deviceID = 0x12344321 + static let timeoutInSeconds : UInt16 = 3 +} + +class MTRSwiftPairingTestControllerDelegate : NSObject, MTRDeviceControllerDelegate { + let expectation: XCTestExpectation + + init(withExpectation providedExpectation: XCTestExpectation) { + expectation = providedExpectation + } + + @available(macOS, introduced: 13.3) + func controller(_ controller: MTRDeviceController, statusUpdate status: MTRCommissioningStatus) { + XCTAssertNotEqual(status, MTRCommissioningStatus.failed) + } + + @available(macOS, introduced: 13.3) + func controller(_ controller: MTRDeviceController, commissioningSessionEstablishmentDone error: Error?) { + XCTAssertNil(error) + + do { + try controller.commissionNode(withID: Constants.deviceID as NSNumber, commissioningParams: MTRCommissioningParameters()) + } catch { + XCTFail("Could not start commissioning of node: \(error)") + } + + // Keep waiting for commissioningComplete + } + + func controller(_ controller: MTRDeviceController, commissioningComplete error: Error?, nodeID: NSNumber?) { + XCTAssertNil(error) + XCTAssertEqual(nodeID, Constants.deviceID as NSNumber) + expectation.fulfill() + } +} + +class MTRSwiftPairingTests : XCTestCase { + @available(macOS, introduced: 13.3) + func test001_BasicPairing() { + let factory = MTRDeviceControllerFactory.sharedInstance() + + let storage = MTRTestStorage() + let factoryParams = MTRDeviceControllerFactoryParams(storage: storage) + factoryParams.port = Constants.localPort as NSNumber + + do { + try factory.start(factoryParams) + } catch { + XCTFail("Could not start controller factory: \(error)") + return + } + XCTAssertTrue(factory.isRunning) + + let testKeys = MTRTestKeys() + + let params = MTRDeviceControllerStartupParams(ipk: testKeys.ipk, fabricID: 1, nocSigner: testKeys) + params.vendorID = Constants.vendorID as NSNumber + + let controller: MTRDeviceController + do { + controller = try factory.createController(onNewFabric: params) + } catch { + XCTFail("Could not create controller: \(error)") + return + } + XCTAssertTrue(controller.isRunning) + + let expectation = expectation(description: "Commissioning Complete") + + let controllerDelegate = MTRSwiftPairingTestControllerDelegate(withExpectation: expectation) + let serialQueue = DispatchQueue(label: "com.chip.pairing") + + controller.setDeviceControllerDelegate(controllerDelegate, queue: serialQueue) + + let payload : MTRSetupPayload + do { + payload = try MTRSetupPayload(onboardingPayload: Constants.onboardingPayload) + } catch { + XCTFail("Could not parse setup payload: \(error)") + return + } + + do { + try controller.setupCommissioningSession(with: payload, newNodeID: Constants.deviceID as NSNumber) + } catch { + XCTFail("Could not start setting up PASE session: \(error)") + return + } + + wait(for: [expectation], timeout: TimeInterval(Constants.timeoutInSeconds)) + + ResetCommissionee(MTRBaseDevice(nodeID: Constants.deviceID as NSNumber, controller: controller), DispatchQueue.main, self, Constants.timeoutInSeconds) + + controller.shutdown() + XCTAssertFalse(controller.isRunning) + + factory.stop() + XCTAssertFalse(factory.isRunning) + } +} diff --git a/src/darwin/Framework/CHIPTests/MatterTests-Bridging-Header.h b/src/darwin/Framework/CHIPTests/MatterTests-Bridging-Header.h new file mode 100644 index 00000000000000..52983e729d27b1 --- /dev/null +++ b/src/darwin/Framework/CHIPTests/MatterTests-Bridging-Header.h @@ -0,0 +1,7 @@ +// +// Use this file to import your target's public headers that you would like to expose to Swift. +// + +#import "MTRTestKeys.h" +#import "MTRTestResetCommissioneeHelper.h" +#import "MTRTestStorage.h" diff --git a/src/darwin/Framework/Matter.xcodeproj/project.pbxproj b/src/darwin/Framework/Matter.xcodeproj/project.pbxproj index 7b97bb8c4861e2..7693dd68e95935 100644 --- a/src/darwin/Framework/Matter.xcodeproj/project.pbxproj +++ b/src/darwin/Framework/Matter.xcodeproj/project.pbxproj @@ -152,6 +152,7 @@ 514304202914CED9004DC7FE /* generic-callback-stubs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5143041F2914CED9004DC7FE /* generic-callback-stubs.cpp */; }; 51431AF927D2973E008A7943 /* MTRIMDispatch.mm in Sources */ = {isa = PBXBuildFile; fileRef = 51431AF827D2973E008A7943 /* MTRIMDispatch.mm */; }; 51431AFB27D29CA4008A7943 /* ota-provider.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 51431AFA27D29CA4008A7943 /* ota-provider.cpp */; }; + 5143851E2A65885500EDC8E6 /* MTRSwiftPairingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5143851D2A65885500EDC8E6 /* MTRSwiftPairingTests.swift */; }; 515C1C6F284F9FFB00A48F0C /* MTRFramework.mm in Sources */ = {isa = PBXBuildFile; fileRef = 515C1C6D284F9FFB00A48F0C /* MTRFramework.mm */; }; 515C1C70284F9FFB00A48F0C /* MTRFramework.h in Headers */ = {isa = PBXBuildFile; fileRef = 515C1C6E284F9FFB00A48F0C /* MTRFramework.h */; }; 51669AF02913204400F4AA36 /* MTRBackwardsCompatTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 51669AEF2913204400F4AA36 /* MTRBackwardsCompatTests.m */; }; @@ -452,6 +453,8 @@ 5143041F2914CED9004DC7FE /* generic-callback-stubs.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "generic-callback-stubs.cpp"; path = "util/generic-callback-stubs.cpp"; sourceTree = ""; }; 51431AF827D2973E008A7943 /* MTRIMDispatch.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MTRIMDispatch.mm; sourceTree = ""; }; 51431AFA27D29CA4008A7943 /* ota-provider.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "ota-provider.cpp"; path = "clusters/ota-provider/ota-provider.cpp"; sourceTree = ""; }; + 5143851C2A65885400EDC8E6 /* MatterTests-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "MatterTests-Bridging-Header.h"; sourceTree = ""; }; + 5143851D2A65885500EDC8E6 /* MTRSwiftPairingTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MTRSwiftPairingTests.swift; sourceTree = ""; }; 515C1C6D284F9FFB00A48F0C /* MTRFramework.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MTRFramework.mm; sourceTree = ""; }; 515C1C6E284F9FFB00A48F0C /* MTRFramework.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MTRFramework.h; sourceTree = ""; }; 51669AEF2913204400F4AA36 /* MTRBackwardsCompatTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MTRBackwardsCompatTests.m; sourceTree = ""; }; @@ -1120,7 +1123,9 @@ 51A2F1312A00402A00F03298 /* MTRDataValueParserTests.m */, 51339B1E2A0DA64D00C798C1 /* MTRCertificateValidityTests.m */, 519498312A25581C00B3BABE /* MTRSetupPayloadSerializerTests.m */, + 5143851D2A65885500EDC8E6 /* MTRSwiftPairingTests.swift */, B202529D2459E34F00F97062 /* Info.plist */, + 5143851C2A65885400EDC8E6 /* MatterTests-Bridging-Header.h */, ); path = CHIPTests; sourceTree = ""; @@ -1346,6 +1351,7 @@ }; B20252952459E34F00F97062 = { CreatedOnToolsVersion = 11.4.1; + LastSwiftMigration = 1430; }; }; }; @@ -1532,6 +1538,7 @@ 1EE0805E2A44875E008A03C2 /* MTRCommissionableBrowserTests.m in Sources */, 51339B1F2A0DA64D00C798C1 /* MTRCertificateValidityTests.m in Sources */, 5173A47929C0E82300F67F48 /* MTRFabricInfoTests.m in Sources */, + 5143851E2A65885500EDC8E6 /* MTRSwiftPairingTests.swift in Sources */, 3D0C484B29DA4FA0006D811F /* MTRErrorTests.m in Sources */, 51742B4A29CB5FC1009974FE /* MTRTestResetCommissioneeHelper.m in Sources */, 5AE6D4E427A99041001F2493 /* MTRDeviceTests.m in Sources */, @@ -1824,6 +1831,7 @@ BA09EB752474881D00605257 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; "HEADER_SEARCH_PATHS[arch=*]" = "$(PROJECT_DIR)/../../../src"; INFOPLIST_FILE = CHIPTests/Info.plist; @@ -1834,6 +1842,9 @@ ); PRODUCT_BUNDLE_IDENTIFIER = com.chip.CHIPTests; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "CHIPTests/MatterTests-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2,3,4"; }; name = Debug; @@ -1987,6 +1998,7 @@ BA09EB792474882200605257 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = ""; "HEADER_SEARCH_PATHS[arch=*]" = "$(PROJECT_DIR)/../../../src"; @@ -1998,6 +2010,8 @@ ); PRODUCT_BUNDLE_IDENTIFIER = com.chip.CHIPTests; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "CHIPTests/MatterTests-Bridging-Header.h"; + SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2,3,4"; }; name = Release;