| /** |
| * |
| * Copyright (c) 2020-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 <Matter/Matter.h> |
| #import <XCTest/XCTest.h> |
| |
| @interface MTRSetupPayloadTests : XCTestCase |
| |
| @end |
| |
| @implementation MTRSetupPayloadTests |
| |
| - (void)testOnboardingPayloadParser_Manual_NoError |
| { |
| NSError * error; |
| MTRSetupPayload * payload = [MTRSetupPayload setupPayloadWithOnboardingPayload:@"641286075300001000016" error:&error]; |
| |
| XCTAssertNotNil(payload); |
| XCTAssertNil(error); |
| |
| XCTAssertTrue(payload.hasShortDiscriminator); |
| XCTAssertEqual(payload.discriminator.unsignedIntegerValue, 10); |
| XCTAssertEqual(payload.setupPasscode.unsignedIntegerValue, 12345670); |
| XCTAssertEqual(payload.vendorID.unsignedIntegerValue, 1); |
| XCTAssertEqual(payload.productID.unsignedIntegerValue, 1); |
| XCTAssertEqual(payload.commissioningFlow, MTRCommissioningFlowCustom); |
| XCTAssertEqual(payload.version.unsignedIntegerValue, 0); |
| XCTAssertEqual(payload.discoveryCapabilities, MTRDiscoveryCapabilitiesUnknown); |
| |
| XCTAssertEqualObjects(payload.manualEntryCode, @"641286075300001000016"); |
| } |
| |
| - (void)testOnboardingPayloadParser_QRCode_NoError |
| { |
| NSError * error; |
| MTRSetupPayload * payload = [MTRSetupPayload setupPayloadWithOnboardingPayload:@"MT:M5L90MP500K64J00000" error:&error]; |
| |
| XCTAssertNotNil(payload); |
| XCTAssertNil(error); |
| |
| XCTAssertFalse(payload.hasShortDiscriminator); |
| XCTAssertEqual(payload.discriminator.unsignedIntegerValue, 128); |
| XCTAssertEqual(payload.setupPasscode.unsignedIntegerValue, 2048); |
| XCTAssertEqual(payload.vendorID.unsignedIntegerValue, 12); |
| XCTAssertEqual(payload.productID.unsignedIntegerValue, 1); |
| XCTAssertEqual(payload.commissioningFlow, MTRCommissioningFlowStandard); |
| XCTAssertEqual(payload.version.unsignedIntegerValue, 0); |
| XCTAssertEqual(payload.discoveryCapabilities, MTRDiscoveryCapabilitiesSoftAP); |
| } |
| |
| - (void)testOnboardingPayloadParser_QRCode_WrongVersion |
| { |
| // Same as testOnboardingPayloadParser_QRCode_NoError, but with version set to 5. |
| NSError * error; |
| MTRSetupPayload * payload = [MTRSetupPayload setupPayloadWithOnboardingPayload:@"MT:R5L90MP500K64J00000" error:&error]; |
| |
| XCTAssertNil(payload); |
| XCTAssertNotNil(error); |
| } |
| |
| - (void)testOnboardingPayloadParser_NFC_NoError |
| { |
| NSError * error; |
| MTRSetupPayload * payload = |
| [MTRSetupPayload setupPayloadWithOnboardingPayload:@"MT:M5L90MP500K64J0A33P0SET70.QT52B.E23-WZE0WISA0DK5N1K8SQ1RYCU1O0" |
| error:&error]; |
| |
| XCTAssertNotNil(payload); |
| XCTAssertNil(error); |
| |
| XCTAssertFalse(payload.hasShortDiscriminator); |
| XCTAssertEqual(payload.discriminator.unsignedIntegerValue, 128); |
| XCTAssertEqual(payload.setupPasscode.unsignedIntegerValue, 2048); |
| XCTAssertEqual(payload.vendorID.unsignedIntegerValue, 12); |
| XCTAssertEqual(payload.productID.unsignedIntegerValue, 1); |
| XCTAssertEqual(payload.commissioningFlow, MTRCommissioningFlowStandard); |
| XCTAssertEqual(payload.version.unsignedIntegerValue, 0); |
| XCTAssertEqual(payload.discoveryCapabilities, MTRDiscoveryCapabilitiesSoftAP); |
| } |
| |
| - (void)testManualParser_Error |
| { |
| NSError * error; |
| MTRSetupPayload * payload = [MTRSetupPayload setupPayloadWithOnboardingPayload:@"" error:&error]; |
| |
| XCTAssertNil(payload); |
| XCTAssertNotNil(error); |
| } |
| |
| - (void)testQRCodeParser_Error |
| { |
| NSError * error; |
| MTRSetupPayload * payload = [MTRSetupPayload setupPayloadWithOnboardingPayload:@"MT:M5L90MP500K64J0000." error:&error]; |
| |
| XCTAssertNil(payload); |
| XCTAssertNotNil(error); |
| } |
| |
| - (void)testQRCodeParser |
| { |
| NSError * error; |
| MTRSetupPayload * payload = [MTRSetupPayload setupPayloadWithOnboardingPayload:@"MT:M5L90MP500K64J00000" error:&error]; |
| |
| XCTAssertNotNil(payload); |
| XCTAssertNil(error); |
| |
| XCTAssertFalse(payload.hasShortDiscriminator); |
| XCTAssertEqual(payload.discriminator.unsignedIntegerValue, 128); |
| XCTAssertEqual(payload.setupPasscode.unsignedIntegerValue, 2048); |
| XCTAssertEqual(payload.vendorID.unsignedIntegerValue, 12); |
| XCTAssertEqual(payload.productID.unsignedIntegerValue, 1); |
| XCTAssertEqual(payload.commissioningFlow, MTRCommissioningFlowStandard); |
| XCTAssertEqual(payload.version.unsignedIntegerValue, 0); |
| XCTAssertEqual(payload.discoveryCapabilities, MTRDiscoveryCapabilitiesSoftAP); |
| } |
| |
| - (void)testQRCodeParserWithOptionalData |
| { |
| NSError * error; |
| MTRSetupPayload * payload = |
| [MTRSetupPayload setupPayloadWithOnboardingPayload:@"MT:M5L90MP500K64J0A33P0SET70.QT52B.E23-WZE0WISA0DK5N1K8SQ1RYCU1O0" |
| error:&error]; |
| |
| XCTAssertNotNil(payload); |
| XCTAssertNil(error); |
| |
| XCTAssertEqual(payload.version.unsignedIntegerValue, 0); |
| XCTAssertFalse(payload.hasShortDiscriminator); |
| XCTAssertEqual(payload.discriminator.unsignedIntegerValue, 128); |
| XCTAssertEqual(payload.setupPasscode.unsignedIntegerValue, 2048); |
| XCTAssertEqual(payload.vendorID.unsignedIntegerValue, 12); |
| XCTAssertEqual(payload.productID.unsignedIntegerValue, 1); |
| XCTAssertEqual(payload.commissioningFlow, MTRCommissioningFlowStandard); |
| XCTAssertEqual(payload.discoveryCapabilities, MTRDiscoveryCapabilitiesSoftAP); |
| XCTAssertTrue([payload.serialNumber isEqualToString:@"123456789"]); |
| |
| NSArray<MTROptionalQRCodeInfo *> * vendorOptionalInfo = [payload getAllOptionalVendorData:&error]; |
| XCTAssertNil(error); |
| XCTAssertEqual([vendorOptionalInfo count], 2); |
| for (MTROptionalQRCodeInfo * info in vendorOptionalInfo) { |
| if (info.tag.intValue == 130) { |
| XCTAssertEqual(info.type, MTROptionalQRCodeInfoTypeString); |
| XCTAssertEqual([info.infoType unsignedIntValue], MTROptionalQRCodeInfoTypeString); |
| XCTAssertTrue([info.stringValue isEqualToString:@"myData"]); |
| } else if (info.tag.intValue == 131) { |
| XCTAssertEqual(info.type, MTROptionalQRCodeInfoTypeInt32); |
| XCTAssertEqual([info.infoType unsignedIntValue], MTROptionalQRCodeInfoTypeInt32); |
| XCTAssertEqual(info.integerValue.intValue, 12); |
| } |
| } |
| |
| // Test access by tag |
| XCTAssertEqualObjects([payload vendorElementWithTag:@130].stringValue, @"myData"); |
| XCTAssertEqualObjects([payload vendorElementWithTag:@131].integerValue, @12); |
| } |
| |
| - (void)testAddVendorElement |
| { |
| MTRSetupPayload * payload = [[MTRSetupPayload alloc] initWithSetupPasscode:@314159 discriminator:@555]; |
| XCTAssertEqual(payload.vendorElements.count, 0); |
| [payload addOrReplaceVendorElement:[[MTROptionalQRCodeInfo alloc] initWithTag:@0xff int32Value:42]]; |
| XCTAssertEqual(payload.vendorElements.count, 1); |
| XCTAssertEqualObjects(payload.vendorElements.firstObject.integerValue, @42); |
| XCTAssertEqualObjects([payload vendorElementWithTag:@0xff].integerValue, @42); |
| } |
| |
| - (void)testVendorElementsEncodedToQRCode |
| { |
| MTRSetupPayload * payload = [[MTRSetupPayload alloc] initWithSetupPasscode:@314159 discriminator:@555]; |
| [payload addOrReplaceVendorElement:[[MTROptionalQRCodeInfo alloc] initWithTag:@0x80 stringValue:@"Hello"]]; |
| MTRSetupPayload * decoded = [[MTRSetupPayload alloc] initWithPayload:payload.qrCodeString]; |
| XCTAssertNotNil(decoded); |
| XCTAssertEqualObjects([decoded vendorElementWithTag:@0x80].stringValue, @"Hello"); |
| } |
| |
| - (void)testRemoveVendorElements |
| { |
| MTRSetupPayload * payload = [[MTRSetupPayload alloc] initWithPayload:@"MT:M5L90MP500K64J0A33P0SET70.QT52B.E23-WZE0WISA0DK5N1K8SQ1RYCU1O0"]; |
| XCTAssertNotNil(payload); |
| XCTAssertEqual(payload.vendorElements.count, 2); |
| [payload removeVendorElementWithTag:@128]; // no change, no vendor element present with this tag |
| XCTAssertEqual(payload.vendorElements.count, 2); |
| [payload removeVendorElementWithTag:@130]; |
| XCTAssertEqual(payload.vendorElements.count, 1); |
| [payload removeVendorElementWithTag:@131]; |
| XCTAssertEqual(payload.vendorElements.count, 0); |
| } |
| |
| - (void)testQrCodeInfoCopyAndEquality |
| { |
| MTROptionalQRCodeInfo * a = [[MTROptionalQRCodeInfo alloc] initWithTag:@0x88 stringValue:@"hello"]; |
| MTROptionalQRCodeInfo * b = [[MTROptionalQRCodeInfo alloc] initWithTag:@0x88 stringValue:@"hello"]; |
| MTROptionalQRCodeInfo * c = [[MTROptionalQRCodeInfo alloc] initWithTag:@0xff stringValue:@"hello"]; |
| MTROptionalQRCodeInfo * d = [[MTROptionalQRCodeInfo alloc] initWithTag:@0x88 int32Value:42]; |
| MTROptionalQRCodeInfo * e = [[MTROptionalQRCodeInfo alloc] initWithTag:@0x88 int32Value:0xbad]; |
| XCTAssertTrue([a isEqual:a]); |
| XCTAssertTrue([a isEqual:[a copy]]); |
| XCTAssertTrue([a isEqual:b]); |
| XCTAssertTrue([a isEqual:[b copy]]); |
| XCTAssertEqual(a.hash, b.hash); |
| XCTAssertEqual(a.hash, [[a copy] hash]); |
| XCTAssertFalse([a isEqual:nil]); |
| XCTAssertFalse([a isEqual:c]); |
| XCTAssertFalse([a isEqual:d]); |
| XCTAssertFalse([a isEqual:e]); |
| XCTAssertFalse([d isEqual:e]); |
| } |
| |
| - (void)testQRCodeWithNoCapabilities |
| { |
| NSError * error; |
| MTRSetupPayload * payload = [MTRSetupPayload setupPayloadWithOnboardingPayload:@"MT:M5L9000000K64J00000" error:&error]; |
| |
| XCTAssertNotNil(payload); |
| XCTAssertNil(error); |
| |
| XCTAssertFalse(payload.hasShortDiscriminator); |
| XCTAssertEqual(payload.discriminator.unsignedIntegerValue, 128); |
| XCTAssertEqual(payload.setupPasscode.unsignedIntegerValue, 2048); |
| XCTAssertEqual(payload.vendorID.unsignedIntegerValue, 12); |
| XCTAssertEqual(payload.productID.unsignedIntegerValue, 1); |
| XCTAssertEqual(payload.commissioningFlow, MTRCommissioningFlowStandard); |
| XCTAssertEqual(payload.version.unsignedIntegerValue, 0); |
| XCTAssertEqual(payload.discoveryCapabilities, MTRDiscoveryCapabilitiesOnNetwork); |
| } |
| |
| - (void)testDeprecatedPropertyAliases |
| { |
| NSError * error; |
| MTRSetupPayload * payload = [MTRSetupPayload setupPayloadWithOnboardingPayload:@"MT:M5L9000000K64J00000" error:&error]; |
| |
| XCTAssertNotNil(payload); |
| XCTAssertNil(error); |
| |
| XCTAssertNotNil(payload.rendezvousInformation); |
| XCTAssertEqual([payload.rendezvousInformation unsignedIntValue], MTRDiscoveryCapabilitiesOnNetwork); |
| XCTAssertEqual(payload.discoveryCapabilities, MTRDiscoveryCapabilitiesOnNetwork); |
| |
| // Check that writing to rendezvousInformation is reflected in |
| // discoveryCapabilities. |
| payload.rendezvousInformation = nil; |
| XCTAssertNil(payload.rendezvousInformation); |
| XCTAssertEqual(payload.discoveryCapabilities, MTRDiscoveryCapabilitiesUnknown); |
| |
| payload.rendezvousInformation = @(MTRDiscoveryCapabilitiesSoftAP); |
| XCTAssertNotNil(payload.rendezvousInformation); |
| XCTAssertEqual([payload.rendezvousInformation unsignedIntValue], MTRDiscoveryCapabilitiesSoftAP); |
| XCTAssertEqual(payload.discoveryCapabilities, MTRDiscoveryCapabilitiesSoftAP); |
| |
| // Check that writing to discoveryCapabilities is reflected in |
| // rendezvousInformation. |
| payload.discoveryCapabilities = MTRDiscoveryCapabilitiesBLE; |
| XCTAssertNotNil(payload.rendezvousInformation); |
| XCTAssertEqual([payload.rendezvousInformation unsignedIntValue], MTRDiscoveryCapabilitiesBLE); |
| XCTAssertEqual(payload.discoveryCapabilities, MTRDiscoveryCapabilitiesBLE); |
| |
| payload.discoveryCapabilities = MTRDiscoveryCapabilitiesUnknown; |
| XCTAssertNil(payload.rendezvousInformation); |
| XCTAssertEqual(payload.discoveryCapabilities, MTRDiscoveryCapabilitiesUnknown); |
| |
| // Check that setupPasscode and setUpPINCode alias each other. |
| payload.setupPasscode = @(1); |
| XCTAssertEqualObjects(payload.setupPasscode, @(1)); |
| XCTAssertEqualObjects(payload.setUpPINCode, @(1)); |
| |
| payload.setUpPINCode = @(2); |
| XCTAssertEqualObjects(payload.setupPasscode, @(2)); |
| XCTAssertEqualObjects(payload.setUpPINCode, @(2)); |
| } |
| |
| - (void)testSerialNumberRoundTrip |
| { |
| NSError * error; |
| MTRSetupPayload * payload = |
| [MTRSetupPayload setupPayloadWithOnboardingPayload:@"MT:M5L90MP500K64J0A33P0SET70.QT52B.E23-WZE0WISA0DK5N1K8SQ1RYCU1O0" |
| error:&error]; |
| XCTAssertNil(error); |
| XCTAssertNotNil(payload); |
| |
| XCTAssertEqualObjects(payload.serialNumber, @"123456789"); |
| |
| NSString * serialNumber = @"12345"; |
| payload.serialNumber = serialNumber; |
| |
| NSString * qrCode = [payload qrCodeString:&error]; |
| XCTAssertNil(error); |
| XCTAssertNotNil(qrCode); |
| |
| MTRSetupPayload * newPayload = [MTRSetupPayload setupPayloadWithOnboardingPayload:qrCode error:&error]; |
| XCTAssertNil(error); |
| XCTAssertNotNil(newPayload); |
| |
| XCTAssertEqualObjects(newPayload.serialNumber, serialNumber); |
| } |
| |
| - (void)test31129 // https://github.com/project-chip/connectedhomeip/issues/31129 |
| { |
| MTRSetupPayload * payload = [[MTRSetupPayload alloc] initWithSetupPasscode:@99999998 discriminator:@3840]; |
| XCTAssertNotNil(payload); |
| // The payload should be representable in at least one of manual or QR format. |
| XCTAssert(payload.manualEntryCode != nil || [payload qrCodeString:NULL] != nil); |
| } |
| |
| - (void)test23357 // https://github.com/project-chip/connectedhomeip/pull/23357 |
| { |
| // Should return nil for invalid payloads (e.g. invalid passcode "@11111111") |
| XCTAssertNil([MTRSetupPayload setupPayloadWithOnboardingPayload:@"MT:-24J042C00KMSP0Z800" error:NULL]); |
| XCTAssertNil([MTRSetupPayload setupPayloadWithOnboardingPayload:@"35191106788" error:NULL]); |
| } |
| |
| - (void)testSecureCodingRoundtrip |
| { |
| NSError * error; |
| NSMutableArray<MTRSetupPayload *> * payloads = [[NSMutableArray alloc] init]; |
| for (NSString * string in @[ |
| @"34970112332", |
| @"641286075300001000016", |
| @"MT:M5L90MP500K64J00000", |
| @"MT:M5L90MP500K64J0A33P0SET70.QT52B.E23-WZE0WISA0DK5N1K8SQ1RYCU1O0" |
| ]) { |
| MTRSetupPayload * payload = [MTRSetupPayload setupPayloadWithOnboardingPayload:string error:&error]; |
| XCTAssertNotNil(payload, @"Error: %@", error); |
| // Ensure our test data has discriminator values that make for the most meaningful round-trip tests. |
| XCTAssert(payload.discriminator.integerValue != 0); |
| XCTAssert(payload.hasShortDiscriminator || payload.discriminator.integerValue > 0xf); |
| [payloads addObject:payload]; |
| } |
| |
| // Also test some other payloads that don't have a valid QR / MPC representation |
| [payloads addObject:[[MTRSetupPayload alloc] init]]; |
| [payloads addObject:[[MTRSetupPayload alloc] initWithSetupPasscode:@22222222 discriminator:@42]]; |
| |
| MTRSetupPayload * futureValues = [[MTRSetupPayload alloc] initWithSetupPasscode:@314159 discriminator:@555]; |
| futureValues.commissioningFlow = 3; // reserved in the spec |
| futureValues.discoveryCapabilities = 0x84; // bits 3-7 reserved in the spec |
| [payloads addObject:futureValues]; |
| |
| for (MTRSetupPayload * payload in payloads) { |
| NSLog(@"Payload: %@", payload); |
| |
| NSData * data = [NSKeyedArchiver archivedDataWithRootObject:payload requiringSecureCoding:YES error:&error]; |
| XCTAssertNotNil(data, @"Error: %@", error); |
| MTRSetupPayload * decoded = [NSKeyedUnarchiver unarchivedObjectOfClass:MTRSetupPayload.class fromData:data error:&error]; |
| XCTAssertNotNil(decoded, @"Error: %@", error); |
| |
| XCTAssertEqualObjects(decoded.version, payload.version); |
| XCTAssertEqualObjects(decoded.vendorID, payload.vendorID); |
| XCTAssertEqualObjects(decoded.productID, payload.productID); |
| XCTAssertEqual(decoded.commissioningFlow, payload.commissioningFlow); |
| XCTAssertEqual(decoded.discoveryCapabilities, payload.discoveryCapabilities); |
| XCTAssertEqual(decoded.hasShortDiscriminator, payload.hasShortDiscriminator); |
| XCTAssertEqualObjects(decoded.discriminator, payload.discriminator); |
| XCTAssertEqualObjects(decoded.setupPasscode, payload.setupPasscode); |
| XCTAssertEqualObjects(decoded.serialNumber, payload.serialNumber); |
| |
| NSArray<MTROptionalQRCodeInfo *> * payloadVDs = [payload getAllOptionalVendorData:&error]; |
| XCTAssertNotNil(payloadVDs, @"Error: %@", error); |
| NSArray<MTROptionalQRCodeInfo *> * decodedVDs = [decoded getAllOptionalVendorData:&error]; |
| XCTAssertNotNil(decodedVDs, @"Error: %@", error); |
| |
| XCTAssertEqual(decodedVDs.count, payloadVDs.count); |
| for (int i = 0; i < decodedVDs.count; i++) { |
| MTROptionalQRCodeInfo * decodedVD = decodedVDs[i]; |
| MTROptionalQRCodeInfo * payloadVD = payloadVDs[i]; |
| XCTAssertEqual(decodedVD.type, payloadVD.type); |
| XCTAssertEqualObjects(decodedVD.tag, payloadVD.tag); |
| XCTAssertEqualObjects(decodedVD.integerValue, payloadVD.integerValue); |
| XCTAssertEqualObjects(decodedVD.stringValue, payloadVD.stringValue); |
| XCTAssertEqualObjects(decodedVD, payloadVD); // also check with isEqual: |
| } |
| |
| // Note that we can't necessarily expect the manualEntryCode and qrCode strings |
| // we generate here to match the original string, but we should get the same |
| // output from the decoded and original objects. |
| XCTAssertEqualObjects([decoded qrCodeString:NULL], [payload qrCodeString:NULL]); |
| XCTAssertEqualObjects(decoded.manualEntryCode, payload.manualEntryCode); |
| } |
| } |
| |
| - (void)testCopyingAndEquality |
| { |
| MTRSetupPayload * payload = [[MTRSetupPayload alloc] initWithPayload:@"MT:M5L9000000K64J00000"]; |
| XCTAssertFalse(payload.hasShortDiscriminator); // came from a QR code |
| XCTAssert(payload.discriminator.integerValue > 0xf); // can't "accidentally" round-trip through a short discriminator |
| |
| MTRSetupPayload * copy = [payload copy]; |
| XCTAssertNotIdentical(payload, copy); // MTRSetupPayload is mutable, must be a new object |
| |
| XCTAssertTrue([payload isEqual:copy]); |
| XCTAssertTrue([copy isEqual:payload]); |
| XCTAssertEqual(payload.hash, copy.hash); |
| |
| copy.hasShortDiscriminator = YES; |
| XCTAssertFalse([copy isEqual:payload]); |
| copy.hasShortDiscriminator = NO; |
| XCTAssertTrue([copy isEqual:payload]); |
| XCTAssertEqual(payload.hash, copy.hash); |
| |
| MTROptionalQRCodeInfo * element = [[MTROptionalQRCodeInfo alloc] initWithTag:@0x80 stringValue:@"To infinity and beyond!"]; |
| [copy addOrReplaceVendorElement:element]; |
| XCTAssertFalse([copy isEqual:payload]); |
| [payload addOrReplaceVendorElement:element]; |
| XCTAssertTrue([copy isEqual:payload]); |
| XCTAssertEqual(payload.hash, copy.hash); |
| |
| copy.serialNumber = @"555-123"; |
| XCTAssertFalse([copy isEqual:payload]); |
| payload.serialNumber = @"555-123"; |
| XCTAssertTrue([copy isEqual:payload]); |
| XCTAssertEqual(payload.hash, copy.hash); |
| } |
| |
| - (void)testCanParseFutureDiscoveryMethod |
| { |
| // We must be able to process QR codes that include discovery methods we don't understand yet |
| XCTAssertEqual([[MTRSetupPayload alloc] initWithPayload:@"MT:000002VDK3VHMR49000"].discoveryCapabilities, 0x84); |
| XCTAssertEqual([[MTRSetupPayload alloc] initWithPayload:@"MT:-24J0Q.C.0KA0648G00"].discoveryCapabilities, 0xfa); |
| } |
| |
| - (void)testDescriptionShowsUnknownDiscoveryMethods |
| { |
| MTRSetupPayload * a = [[MTRSetupPayload alloc] initWithSetupPasscode:@888 discriminator:@555]; |
| MTRSetupPayload * b = [a copy]; |
| b.discoveryCapabilities |= 0x80; |
| XCTAssertNotEqualObjects(a.description, b.description); |
| } |
| |
| @end |