From 2b8f5d7669a980b1b3ac93c37efaa31ed987c82f Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Fri, 13 Dec 2024 12:31:07 -0500 Subject: [PATCH] Add MTRErrorDomain mappings for a few more CHIP_ERROR values. (#36835) Also adds some basic round-tripping tests to catch mistakes. --- src/darwin/Framework/CHIP/MTRError.h | 79 +++++++++++-------- src/darwin/Framework/CHIP/MTRError.mm | 38 +++++++++ src/darwin/Framework/CHIP/MTRError_Internal.h | 5 +- src/darwin/Framework/CHIP/MTRError_Testable.h | 37 +++++++++ .../CHIPTests/MTRErrorMappingTests.m | 60 ++++++++++++++ .../Matter.xcodeproj/project.pbxproj | 8 ++ 6 files changed, 191 insertions(+), 36 deletions(-) create mode 100644 src/darwin/Framework/CHIP/MTRError_Testable.h create mode 100644 src/darwin/Framework/CHIPTests/MTRErrorMappingTests.m diff --git a/src/darwin/Framework/CHIP/MTRError.h b/src/darwin/Framework/CHIP/MTRError.h index 428464dab983ef..cf359fac960979 100644 --- a/src/darwin/Framework/CHIP/MTRError.h +++ b/src/darwin/Framework/CHIP/MTRError.h @@ -35,7 +35,6 @@ MTR_EXTERN NSErrorDomain const MTRInteractionErrorDomain MTR_AVAILABLE(ios(16.1) * Errors reported by the server side of a Matter interaction via the normal * Matter error-reporting mechanisms use MTRInteractionErrorDomain instead. */ -// clang-format off typedef NS_ERROR_ENUM(MTRErrorDomain, MTRErrorCode){ /** * MTRErrorCodeGeneralError represents a generic Matter error with no @@ -61,7 +60,7 @@ typedef NS_ERROR_ENUM(MTRErrorDomain, MTRErrorCode){ * MTRErrorCodeFabricExists is returned when trying to commission a device * into a fabric when it's already part of that fabric. */ - MTRErrorCodeFabricExists = 11, + MTRErrorCodeFabricExists = 11, /** * MTRErrorCodeUnknownSchema means the schema for the given cluster/attribute, @@ -95,9 +94,25 @@ typedef NS_ERROR_ENUM(MTRErrorDomain, MTRErrorCode){ /** * The operation was cancelled. */ - MTRErrorCodeCancelled MTR_AVAILABLE(ios(17.6), macos(14.6), watchos(10.6), tvos(17.6))= 16, + MTRErrorCodeCancelled MTR_AVAILABLE(ios(17.6), macos(14.6), watchos(10.6), tvos(17.6)) = 16, + + /** + * Access to some resource was denied. + */ + MTRErrorCodeAccessDenied MTR_AVAILABLE(ios(18.4), macos(15.4), watchos(11.4), tvos(18.4)) = 17, + + /** + * A request was made to some entity, and that entity cannot handle the + * request right now, but might be able to at a different point in time. + */ + MTRErrorCodeBusy MTR_AVAILABLE(ios(18.4), macos(15.4), watchos(11.4), tvos(18.4)) = 18, + + /** + * Something was requested that could not be located. + */ + MTRErrorCodeNotFound MTR_AVAILABLE(ios(18.4), macos(15.4), watchos(11.4), tvos(18.4)) = 19, }; -// clang-format on +#define MTRMaxErrorCode MTRErrorCodeNotFound /** * MTRInteractionErrorDomain contains errors that represent a Matter @@ -109,38 +124,36 @@ typedef NS_ERROR_ENUM(MTRErrorDomain, MTRErrorCode){ * was reported. This key will be absent if there was no cluster-specific * status. */ -// clang-format off typedef NS_ERROR_ENUM(MTRInteractionErrorDomain, MTRInteractionErrorCode){ // These values come from the general status code table in the Matter // Interaction Model specification. - MTRInteractionErrorCodeFailure = 0x01, - MTRInteractionErrorCodeInvalidSubscription = 0x7d, - MTRInteractionErrorCodeUnsupportedAccess = 0x7e, - MTRInteractionErrorCodeUnsupportedEndpoint = 0x7f, - MTRInteractionErrorCodeInvalidAction = 0x80, - MTRInteractionErrorCodeUnsupportedCommand = 0x81, - MTRInteractionErrorCodeInvalidCommand = 0x85, - MTRInteractionErrorCodeUnsupportedAttribute = 0x86, - MTRInteractionErrorCodeConstraintError = 0x87, - MTRInteractionErrorCodeUnsupportedWrite = 0x88, - MTRInteractionErrorCodeResourceExhausted = 0x89, - MTRInteractionErrorCodeNotFound = 0x8b, - MTRInteractionErrorCodeUnreportableAttribute = 0x8c, - MTRInteractionErrorCodeInvalidDataType = 0x8d, - MTRInteractionErrorCodeUnsupportedRead = 0x8f, - MTRInteractionErrorCodeDataVersionMismatch = 0x92, - MTRInteractionErrorCodeTimeout = 0x94, - MTRInteractionErrorCodeBusy = 0x9c, - MTRInteractionErrorCodeUnsupportedCluster = 0xc3, - MTRInteractionErrorCodeNoUpstreamSubscription = 0xc5, - MTRInteractionErrorCodeNeedsTimedInteraction = 0xc6, - MTRInteractionErrorCodeUnsupportedEvent = 0xc7, - MTRInteractionErrorCodePathsExhausted = 0xc8, - MTRInteractionErrorCodeTimedRequestMismatch = 0xc9, - MTRInteractionErrorCodeFailsafeRequired = 0xca, - MTRInteractionErrorCodeInvalidInState MTR_AVAILABLE(ios(17.6), macos(14.6), watchos(10.6), tvos(17.6))= 0xcb, - MTRInteractionErrorCodeNoCommandResponse MTR_AVAILABLE(ios(17.6), macos(14.6), watchos(10.6), tvos(17.6))= 0xcc, + MTRInteractionErrorCodeFailure = 0x01, + MTRInteractionErrorCodeInvalidSubscription = 0x7d, + MTRInteractionErrorCodeUnsupportedAccess = 0x7e, + MTRInteractionErrorCodeUnsupportedEndpoint = 0x7f, + MTRInteractionErrorCodeInvalidAction = 0x80, + MTRInteractionErrorCodeUnsupportedCommand = 0x81, + MTRInteractionErrorCodeInvalidCommand = 0x85, + MTRInteractionErrorCodeUnsupportedAttribute = 0x86, + MTRInteractionErrorCodeConstraintError = 0x87, + MTRInteractionErrorCodeUnsupportedWrite = 0x88, + MTRInteractionErrorCodeResourceExhausted = 0x89, + MTRInteractionErrorCodeNotFound = 0x8b, + MTRInteractionErrorCodeUnreportableAttribute = 0x8c, + MTRInteractionErrorCodeInvalidDataType = 0x8d, + MTRInteractionErrorCodeUnsupportedRead = 0x8f, + MTRInteractionErrorCodeDataVersionMismatch = 0x92, + MTRInteractionErrorCodeTimeout = 0x94, + MTRInteractionErrorCodeBusy = 0x9c, + MTRInteractionErrorCodeUnsupportedCluster = 0xc3, + MTRInteractionErrorCodeNoUpstreamSubscription = 0xc5, + MTRInteractionErrorCodeNeedsTimedInteraction = 0xc6, + MTRInteractionErrorCodeUnsupportedEvent = 0xc7, + MTRInteractionErrorCodePathsExhausted = 0xc8, + MTRInteractionErrorCodeTimedRequestMismatch = 0xc9, + MTRInteractionErrorCodeFailsafeRequired = 0xca, + MTRInteractionErrorCodeInvalidInState MTR_AVAILABLE(ios(17.6), macos(14.6), watchos(10.6), tvos(17.6)) = 0xcb, + MTRInteractionErrorCodeNoCommandResponse MTR_AVAILABLE(ios(17.6), macos(14.6), watchos(10.6), tvos(17.6)) = 0xcc, }; -// clang-format on NS_ASSUME_NONNULL_END diff --git a/src/darwin/Framework/CHIP/MTRError.mm b/src/darwin/Framework/CHIP/MTRError.mm index e268ab49e32cc9..e7f3acb90ad5ba 100644 --- a/src/darwin/Framework/CHIP/MTRError.mm +++ b/src/darwin/Framework/CHIP/MTRError.mm @@ -107,6 +107,10 @@ + (NSError *)errorForCHIPErrorCode:(CHIP_ERROR)errorCode logContext:(id)contextT code = MTRErrorCodeFabricExists; description = NSLocalizedString(@"The device is already a member of this fabric.", nil); break; + case CHIP_ERROR_SCHEMA_MISMATCH.AsInteger(): + code = MTRErrorCodeSchemaMismatch; + description = NSLocalizedString(@"Data does not match expected schema.", nil); + break; case CHIP_ERROR_DECODE_FAILED.AsInteger(): code = MTRErrorCodeTLVDecodeFailed; description = NSLocalizedString(@"TLV decoding failed.", nil); @@ -122,6 +126,18 @@ + (NSError *)errorForCHIPErrorCode:(CHIP_ERROR)errorCode logContext:(id)contextT code = MTRErrorCodeCancelled; description = NSLocalizedString(@"The operation was cancelled.", nil); break; + case CHIP_ERROR_ACCESS_DENIED.AsInteger(): + code = MTRErrorCodeAccessDenied; + description = NSLocalizedString(@"Access denied.", nil); + break; + case CHIP_ERROR_BUSY.AsInteger(): + code = MTRErrorCodeBusy; + description = NSLocalizedString(@"Operation cannot be completed at this time: resource busy.", nil); + break; + case CHIP_ERROR_NOT_FOUND.AsInteger(): + code = MTRErrorCodeNotFound; + description = NSLocalizedString(@"Requested resource was not found.", nil); + break; default: code = MTRErrorCodeGeneralError; description = [NSString stringWithFormat:NSLocalizedString(@"General error: %u", nil), errorCode.AsInteger()]; @@ -141,6 +157,11 @@ + (NSError *)errorForCHIPErrorCode:(CHIP_ERROR)errorCode logContext:(id)contextT return error; } ++ (NSError *)errorForCHIPIntegerCode:(uint32_t)errorCode +{ + return [MTRError errorForCHIPErrorCode:chip::ChipError(errorCode)]; +} + + (NSError *)errorForIMStatus:(const chip::app::StatusIB &)status { if (status.IsSuccess()) { @@ -303,6 +324,9 @@ + (CHIP_ERROR)errorToCHIPErrorCode:(NSError * _Nullable)error case MTRErrorCodeFabricExists: code = CHIP_ERROR_FABRIC_EXISTS.AsInteger(); break; + case MTRErrorCodeSchemaMismatch: + code = CHIP_ERROR_SCHEMA_MISMATCH.AsInteger(); + break; case MTRErrorCodeTLVDecodeFailed: code = CHIP_ERROR_DECODE_FAILED.AsInteger(); break; @@ -312,6 +336,15 @@ + (CHIP_ERROR)errorToCHIPErrorCode:(NSError * _Nullable)error case MTRErrorCodeCancelled: code = CHIP_ERROR_CANCELLED.AsInteger(); break; + case MTRErrorCodeAccessDenied: + code = CHIP_ERROR_ACCESS_DENIED.AsInteger(); + break; + case MTRErrorCodeBusy: + code = CHIP_ERROR_BUSY.AsInteger(); + break; + case MTRErrorCodeNotFound: + code = CHIP_ERROR_NOT_FOUND.AsInteger(); + break; case MTRErrorCodeGeneralError: { id userInfoErrorCode = error.userInfo[@"errorCode"]; if ([userInfoErrorCode isKindOfClass:NSNumber.class]) { @@ -328,6 +361,11 @@ + (CHIP_ERROR)errorToCHIPErrorCode:(NSError * _Nullable)error return chip::ChipError(code); } ++ (uint32_t)errorToCHIPIntegerCode:(NSError * _Nullable)error +{ + return [self errorToCHIPErrorCode:error].AsInteger(); +} + @end @implementation MTRErrorHolder diff --git a/src/darwin/Framework/CHIP/MTRError_Internal.h b/src/darwin/Framework/CHIP/MTRError_Internal.h index c79cc17d95f198..5bb61116430958 100644 --- a/src/darwin/Framework/CHIP/MTRError_Internal.h +++ b/src/darwin/Framework/CHIP/MTRError_Internal.h @@ -16,9 +16,9 @@ */ #import -#import #import "MTRDefines_Internal.h" +#import "MTRError_Testable.h" #include #include @@ -27,8 +27,7 @@ NS_ASSUME_NONNULL_BEGIN MTR_DIRECT_MEMBERS -@interface MTRError : NSObject -+ (NSError *)errorWithCode:(MTRErrorCode)code; +@interface MTRError () + (NSError * _Nullable)errorForCHIPErrorCode:(CHIP_ERROR)errorCode; + (NSError * _Nullable)errorForCHIPErrorCode:(CHIP_ERROR)errorCode logContext:(id _Nullable)contextToLog; + (NSError * _Nullable)errorForIMStatus:(const chip::app::StatusIB &)status; diff --git a/src/darwin/Framework/CHIP/MTRError_Testable.h b/src/darwin/Framework/CHIP/MTRError_Testable.h new file mode 100644 index 00000000000000..a4456c6097ee01 --- /dev/null +++ b/src/darwin/Framework/CHIP/MTRError_Testable.h @@ -0,0 +1,37 @@ +/** + * Copyright (c) 2024 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 +#import + +#import "MTRDefines_Internal.h" + +NS_ASSUME_NONNULL_BEGIN + +MTR_TESTABLE +@interface MTRError : NSObject + ++ (NSError *)errorWithCode:(MTRErrorCode)code; + +// For tests only, since we can't use CHIP_ERROR from there. The "code"s used +// here are integer representations of CHIP_ERROR. Otherwise these functions +// are just like errorForCHIPErrorCode and errorToCHIPErrorCode. ++ (NSError *)errorForCHIPIntegerCode:(uint32_t)code; ++ (uint32_t)errorToCHIPIntegerCode:(NSError * _Nullable)error; + +@end + +NS_ASSUME_NONNULL_END diff --git a/src/darwin/Framework/CHIPTests/MTRErrorMappingTests.m b/src/darwin/Framework/CHIPTests/MTRErrorMappingTests.m new file mode 100644 index 00000000000000..3bcc6ba8046fd7 --- /dev/null +++ b/src/darwin/Framework/CHIPTests/MTRErrorMappingTests.m @@ -0,0 +1,60 @@ +/** + * Copyright (c) 2024 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 +#import + +// NOTE: This is a .mm file, so that it can include MTRError_Internal.h + +#import "MTRError_Testable.h" + +@interface MTRErrorMappingTests : XCTestCase +@end + +@implementation MTRErrorMappingTests + +- (void)testPublicNonInteractionAPIValues +{ + for (int errorCode = 1; errorCode <= MTRMaxErrorCode; errorCode++) { + // A few error codes are not actually representing CHIP_ERROR values. + if (errorCode == MTRErrorCodeWrongAddressType || errorCode == MTRErrorCodeUnknownSchema) { + continue; + } + + // All of these should round-trip appropriately. + __auto_type * error = [NSError errorWithDomain:MTRErrorDomain code:errorCode userInfo:nil]; + __auto_type * newError1 = [MTRError errorWithCode:(MTRErrorCode) errorCode]; + XCTAssertEqual(newError1.domain, error.domain, "Testing error code %d", errorCode); + XCTAssertEqual(newError1.code, error.code, "Testing error code %d", errorCode); + + __auto_type chipError = [MTRError errorToCHIPIntegerCode:error]; + __auto_type * newError2 = [MTRError errorForCHIPIntegerCode:chipError]; + XCTAssertEqual(newError2.domain, error.domain, "Testing error code %d", errorCode); + XCTAssertEqual(newError2.code, error.code, "Testing error code %d", errorCode); + } + + // Check that an unknown value becomes GeneralError. + __auto_type * error = [MTRError errorWithCode:(MTRErrorCode) (MTRMaxErrorCode + 1)]; + XCTAssertEqual(error.domain, MTRErrorDomain); + XCTAssertEqual(error.code, MTRMaxErrorCode + 1); + + __auto_type chipError = [MTRError errorToCHIPIntegerCode:error]; + __auto_type * newError = [MTRError errorForCHIPIntegerCode:chipError]; + XCTAssertEqual(newError.domain, MTRErrorDomain); + XCTAssertEqual(newError.code, MTRErrorCodeGeneralError); +} + +@end diff --git a/src/darwin/Framework/Matter.xcodeproj/project.pbxproj b/src/darwin/Framework/Matter.xcodeproj/project.pbxproj index 1de3c69f2f1417..f2b89f0ca48a3f 100644 --- a/src/darwin/Framework/Matter.xcodeproj/project.pbxproj +++ b/src/darwin/Framework/Matter.xcodeproj/project.pbxproj @@ -178,6 +178,8 @@ 51565CB22A7AD77600469F18 /* MTRDeviceControllerDataStore.mm in Sources */ = {isa = PBXBuildFile; fileRef = 51565CB02A7AD77600469F18 /* MTRDeviceControllerDataStore.mm */; }; 51565CB42A7AD78D00469F18 /* MTRDeviceControllerStorageDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 51565CB32A7AD78D00469F18 /* MTRDeviceControllerStorageDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; 51565CB62A7B0D6600469F18 /* MTRDeviceControllerParameters.h in Headers */ = {isa = PBXBuildFile; fileRef = 51565CB52A7B0D6600469F18 /* MTRDeviceControllerParameters.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 51578AE92D0B9B1D001716FF /* MTRErrorMappingTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 51578AE82D0B9B1D001716FF /* MTRErrorMappingTests.m */; }; + 51578AEB2D0B9DC0001716FF /* MTRError_Testable.h in Headers */ = {isa = PBXBuildFile; fileRef = 51578AEA2D0B9DC0001716FF /* MTRError_Testable.h */; }; 515BE4ED2B72C0C5000BC1FD /* MTRUnfairLock.h in Headers */ = {isa = PBXBuildFile; fileRef = 515BE4EC2B72C0C5000BC1FD /* MTRUnfairLock.h */; }; 515C1C6F284F9FFB00A48F0C /* MTRFramework.mm in Sources */ = {isa = PBXBuildFile; fileRef = 515C1C6D284F9FFB00A48F0C /* MTRFramework.mm */; }; 515C1C70284F9FFB00A48F0C /* MTRFramework.h in Headers */ = {isa = PBXBuildFile; fileRef = 515C1C6E284F9FFB00A48F0C /* MTRFramework.h */; }; @@ -669,6 +671,8 @@ 51565CB02A7AD77600469F18 /* MTRDeviceControllerDataStore.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MTRDeviceControllerDataStore.mm; sourceTree = ""; }; 51565CB32A7AD78D00469F18 /* MTRDeviceControllerStorageDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MTRDeviceControllerStorageDelegate.h; sourceTree = ""; }; 51565CB52A7B0D6600469F18 /* MTRDeviceControllerParameters.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MTRDeviceControllerParameters.h; sourceTree = ""; }; + 51578AE82D0B9B1D001716FF /* MTRErrorMappingTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MTRErrorMappingTests.m; sourceTree = ""; }; + 51578AEA2D0B9DC0001716FF /* MTRError_Testable.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MTRError_Testable.h; sourceTree = ""; }; 515BE4EC2B72C0C5000BC1FD /* MTRUnfairLock.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MTRUnfairLock.h; 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 = ""; }; @@ -1540,6 +1544,7 @@ 5109E9B32CB8B5DF0006884B /* MTRDeviceType.mm */, 5129BCFC26A9EE3300122DDF /* MTRError.h */, B2E0D7AB245B0B5C003C5B48 /* MTRError_Internal.h */, + 51578AEA2D0B9DC0001716FF /* MTRError_Testable.h */, B2E0D7AA245B0B5C003C5B48 /* MTRError.mm */, 754F3DF327FBB94B00E60580 /* MTREventTLVValueDecoder_Internal.h */, 515C1C6E284F9FFB00A48F0C /* MTRFramework.h */, @@ -1618,6 +1623,7 @@ 5109E9B62CB8B83D0006884B /* MTRDeviceTypeTests.m */, 51D9CB0A2BA37DCE0049D6DB /* MTRDSTOffsetTests.m */, 3D0C484A29DA4FA0006D811F /* MTRErrorTests.m */, + 51578AE82D0B9B1D001716FF /* MTRErrorMappingTests.m */, 5173A47829C0E82300F67F48 /* MTRFabricInfoTests.m */, 8874C1312B69C7060084BEFD /* MTRMetricsTests.m */, 510CECA6297F72470064E0B3 /* MTROperationalCertificateIssuerTests.m */, @@ -1941,6 +1947,7 @@ 9B5CCB5D2C6EC890009DD99B /* MTRDevice_XPC.h in Headers */, 991DC08B247704DC00C13860 /* MTRLogging_Internal.h in Headers */, 51FE723F2ACDEF3E00437032 /* MTRCommandPayloadExtensions_Internal.h in Headers */, + 51578AEB2D0B9DC0001716FF /* MTRError_Testable.h in Headers */, 51D0B12E2B6177FD006E3511 /* MTRAccessGrant.h in Headers */, 5109E9B52CB8B5DF0006884B /* MTRDeviceType.h in Headers */, 1E4D655029C208DD00BC3478 /* MTRCommissionableBrowserDelegate.h in Headers */, @@ -2348,6 +2355,7 @@ 518D3F852AA14006008E0007 /* MTRControllerAdvertisingTests.m in Sources */, 51C8E3F82825CDB600D47D00 /* MTRTestKeys.m in Sources */, 51C984622A61CE2A00B0AD9A /* MTRFabricInfoChecker.m in Sources */, + 51578AE92D0B9B1D001716FF /* MTRErrorMappingTests.m in Sources */, 99C65E10267282F1003402F6 /* MTRControllerTests.m in Sources */, 8874C1322B69C7060084BEFD /* MTRMetricsTests.m in Sources */, 1E5801C328941C050033A199 /* MTRTestOTAProvider.m in Sources */,