@@ -3350,14 +3350,23 @@ - (void)test031_MTRDeviceAttributeCacheLocalTestStorage
3350
3350
XCTAssertTrue (storedAttributeCountDifferenceFromMTRDeviceReport > 300 );
3351
3351
}
3352
3352
3353
- - (void ) doEncodeDecodeRoundTrip : (id <NSSecureCoding >)encodable
3353
+ - (NSData *) _encodeEncodable : (id <NSSecureCoding >)encodable
3354
3354
{
3355
3355
// We know all our encodables are in fact NSObject.
3356
3356
NSObject * obj = (NSObject *) encodable;
3357
3357
3358
3358
NSError * encodeError;
3359
3359
NSData * encodedData = [NSKeyedArchiver archivedDataWithRootObject: encodable requiringSecureCoding: YES error: &encodeError];
3360
3360
XCTAssertNil (encodeError, @" Failed to encode %@ " , NSStringFromClass (obj.class ));
3361
+ return encodedData;
3362
+ }
3363
+
3364
+ - (void )doEncodeDecodeRoundTrip : (id <NSSecureCoding >)encodable
3365
+ {
3366
+ NSData * encodedData = [self _encodeEncodable: encodable];
3367
+
3368
+ // We know all our encodables are in fact NSObject.
3369
+ NSObject * obj = (NSObject *) encodable;
3361
3370
3362
3371
NSError * decodeError;
3363
3372
id decodedValue = [NSKeyedUnarchiver unarchivedObjectOfClasses: [NSSet setWithObject: obj.class] fromData: encodedData error: &decodeError];
@@ -3367,6 +3376,19 @@ - (void)doEncodeDecodeRoundTrip:(id<NSSecureCoding>)encodable
3367
3376
XCTAssertEqualObjects (obj, decodedValue, @" Decoding for %@ did not round-trip correctly" , NSStringFromClass ([obj class ]));
3368
3377
}
3369
3378
3379
+ - (void )_ensureDecodeFails : (id <NSSecureCoding >)encodable
3380
+ {
3381
+ NSData * encodedData = [self _encodeEncodable: encodable];
3382
+
3383
+ // We know all our encodables are in fact NSObject.
3384
+ NSObject * obj = (NSObject *) encodable;
3385
+
3386
+ NSError * decodeError;
3387
+ id decodedValue = [NSKeyedUnarchiver unarchivedObjectOfClasses: [NSSet setWithObject: obj.class] fromData: encodedData error: &decodeError];
3388
+ XCTAssertNil (decodedValue);
3389
+ XCTAssertNotNil (decodeError);
3390
+ }
3391
+
3370
3392
- (void )test032_MTRPathClassesEncoding
3371
3393
{
3372
3394
// Test attribute path encode / decode
@@ -6023,6 +6045,121 @@ - (void)test045_MTRDeviceInvokeGroups
6023
6045
[self waitForExpectations: @[ updateFabricLabelExpectingWrongValueExpectation ] timeout: (2 * kTimeoutInSeconds )];
6024
6046
}
6025
6047
6048
+ - (void )test046_MTRCommandWithRequiredResponseEncoding
6049
+ {
6050
+ // Basic test with no command fields or required response.
6051
+ __auto_type * onPath = [MTRCommandPath commandPathWithEndpointID: @(1 )
6052
+ clusterID: @(MTRClusterIDTypeOnOffID)
6053
+ commandID: @(MTRCommandIDTypeClusterOnOffCommandOnID)];
6054
+ __auto_type * onCommand = [[MTRCommandWithRequiredResponse alloc ] initWithPath: onPath commandFields: nil requiredResponse: nil ];
6055
+ [self doEncodeDecodeRoundTrip: onCommand];
6056
+
6057
+ // Test with both command fields and an interesting required response.
6058
+ //
6059
+ // NSSecureCoding tracks object identity, so we need to create new objects
6060
+ // for every instance of a thing we decode/encode with a given coder to make
6061
+ // sure all codepaths are exercised. Use a block that returns a new
6062
+ // dictionary each time to handle this.
6063
+ __auto_type structureWithAllTypes = ^{
6064
+ return @{
6065
+ MTRTypeKey : MTRStructureValueType,
6066
+ MTRValueKey : @[
6067
+ @{
6068
+ MTRContextTagKey : @(0 ),
6069
+ MTRDataKey : @ {
6070
+ MTRTypeKey : MTRSignedIntegerValueType,
6071
+ MTRValueKey : @(5 ),
6072
+ },
6073
+ },
6074
+ @{
6075
+ MTRContextTagKey : @(1 ),
6076
+ MTRDataKey : @ {
6077
+ MTRTypeKey : MTRUnsignedIntegerValueType,
6078
+ MTRValueKey : @(5 ),
6079
+ },
6080
+ },
6081
+ @{
6082
+ MTRContextTagKey : @(2 ),
6083
+ MTRDataKey : @ {
6084
+ MTRTypeKey : MTRBooleanValueType,
6085
+ MTRValueKey : @(YES ),
6086
+ },
6087
+ },
6088
+ @{
6089
+ MTRContextTagKey : @(3 ),
6090
+ MTRDataKey : @ {
6091
+ MTRTypeKey : MTRUTF8StringValueType,
6092
+ MTRValueKey : @(" abc" ),
6093
+ },
6094
+ },
6095
+ @{
6096
+ MTRContextTagKey : @(4 ),
6097
+ MTRDataKey : @ {
6098
+ MTRTypeKey : MTROctetStringValueType,
6099
+ MTRValueKey : [[NSData alloc ] initWithBase64EncodedString: @" APJj" options: 0 ],
6100
+ },
6101
+ },
6102
+ @{
6103
+ MTRContextTagKey : @(5 ),
6104
+ MTRDataKey : @ {
6105
+ MTRTypeKey : MTRFloatValueType,
6106
+ MTRValueKey : @(1.0 ),
6107
+ },
6108
+ },
6109
+ @{
6110
+ MTRContextTagKey : @(6 ),
6111
+ MTRDataKey : @ {
6112
+ MTRTypeKey : MTRDoubleValueType,
6113
+ MTRValueKey : @(5.0 ),
6114
+ },
6115
+ },
6116
+ @{
6117
+ MTRContextTagKey : @(7 ),
6118
+ MTRDataKey : @ {
6119
+ MTRTypeKey : MTRNullValueType,
6120
+ },
6121
+ },
6122
+ @{
6123
+ MTRContextTagKey : @(8 ),
6124
+ MTRDataKey : @ {
6125
+ MTRTypeKey : MTRArrayValueType,
6126
+ MTRValueKey : @[
6127
+ @{
6128
+ MTRDataKey : @ {
6129
+ MTRTypeKey : MTRUnsignedIntegerValueType,
6130
+ MTRValueKey : @(9 ),
6131
+ },
6132
+ },
6133
+ ],
6134
+ }
6135
+ },
6136
+ ],
6137
+ };
6138
+ };
6139
+
6140
+ // Invalid commandFields (not a dictionary)
6141
+ onCommand.commandFields = (id ) @[];
6142
+ [self _ensureDecodeFails: onCommand];
6143
+
6144
+ // Invalid required response (not a dictionary)
6145
+ onCommand.commandFields = nil ;
6146
+ onCommand.requiredResponse = (id ) @[];
6147
+ [self _ensureDecodeFails: onCommand];
6148
+
6149
+ // Invalid required response (key is not NSNumber)
6150
+ onCommand.requiredResponse = @{
6151
+ @(" abc" ) : structureWithAllTypes (),
6152
+ };
6153
+ [self _ensureDecodeFails: onCommand];
6154
+
6155
+ onCommand.commandFields = structureWithAllTypes ();
6156
+ onCommand.requiredResponse = @{
6157
+ @(1 ) : structureWithAllTypes (),
6158
+ @(13 ) : structureWithAllTypes (),
6159
+ };
6160
+ [self doEncodeDecodeRoundTrip: onCommand];
6161
+ }
6162
+
6026
6163
@end
6027
6164
6028
6165
@interface MTRDeviceEncoderTests : XCTestCase
0 commit comments