@@ -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