Skip to content

Commit

Permalink
Darwin: Add more tests and tidy up MTRSetupPayload (#33234)
Browse files Browse the repository at this point in the history
* Darwin Tests: Make MTRSetupPayload initialization test reliable

Also rename the tests to more conventional names.

* Add regression test for #31129

* Add regression test for #23357

* Add test for NSSecureCoding support

* Darwin: Tidy up MTRSetupPayload headers / includes

* Darwin: return nil on error in MTRSetupPayload getAllOptionalVendorData
  • Loading branch information
ksperling-apple authored Apr 30, 2024
1 parent ee53359 commit 85d4114
Show file tree
Hide file tree
Showing 5 changed files with 129 additions and 44 deletions.
17 changes: 10 additions & 7 deletions src/darwin/Framework/CHIP/MTRSetupPayload.mm
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/**
*
* Copyright (c) 2020 Project CHIP Authors
* 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.
Expand All @@ -15,20 +15,21 @@
* limitations under the License.
*/

#import "MTRError.h"
#import "MTRSetupPayload_Internal.h"

#import "MTRError_Internal.h"
#import "MTRFramework.h"
#import "MTROnboardingPayloadParser.h"
#import "MTRSetupPayload_Internal.h"
#import "setup_payload/ManualSetupPayloadGenerator.h"
#import "setup_payload/QRCodeSetupPayloadGenerator.h"
#import <setup_payload/SetupPayload.h>

#include <setup_payload/ManualSetupPayloadGenerator.h>
#include <setup_payload/QRCodeSetupPayloadGenerator.h>
#include <setup_payload/SetupPayload.h>
#include <string>

@implementation MTROptionalQRCodeInfo
@end

MTR_DIRECT_MEMBERS
@implementation MTRSetupPayload {
chip::SetupPayload _chipSetupPayload;
}
Expand Down Expand Up @@ -182,7 +183,7 @@ - (void)getSerialNumber:(chip::SetupPayload)setupPayload
if (error) {
*error = [NSError errorWithDomain:MTRErrorDomain code:MTRErrorCodeInvalidArgument userInfo:nil];
}
return @[];
return nil;
}
[allOptionalData addObject:info];
}
Expand Down Expand Up @@ -418,6 +419,7 @@ + (MTRDiscoveryCapabilities)_unboxDiscoveryCapabilities:(nullable NSNumber *)box

@end

MTR_DIRECT_MEMBERS
@implementation MTROptionalQRCodeInfo (Deprecated)

- (NSNumber *)infoType
Expand All @@ -432,6 +434,7 @@ - (void)setInfoType:(NSNumber *)infoType

@end

MTR_DIRECT_MEMBERS
@implementation MTRSetupPayload (Deprecated)

- (nullable NSNumber *)rendezvousInformation
Expand Down
27 changes: 19 additions & 8 deletions src/darwin/Framework/CHIP/MTRSetupPayload_Internal.h
Original file line number Diff line number Diff line change
@@ -1,19 +1,30 @@
//
// MTRSetupPayload_Internal.h
// MTR
//
// Copyright © 2021 CHIP. All rights reserved.
//

#import <Foundation/Foundation.h>
/**
*
* Copyright (c) 2021-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 "MTRSetupPayload.h"

#import "MTRDefines_Internal.h"

#ifdef __cplusplus
#import <lib/core/Optional.h>
#import <setup_payload/SetupPayload.h>
#endif

MTR_DIRECT_MEMBERS
@interface MTRSetupPayload ()

#ifdef __cplusplus
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,25 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// module headers
#import "MTRSetupPayload.h"

// additional includes
#import "MTRError.h"

// system dependencies
#import <Matter/Matter.h>
#import <XCTest/XCTest.h>

@interface MTRSetupPayloadSerializerTests : XCTestCase
@interface MTRSetupPayloadInitializationTests : XCTestCase

@end

@implementation MTRSetupPayloadSerializerTests
@implementation MTRSetupPayloadInitializationTests

- (BOOL)shouldRelaunchBeforeRunningTest
{
// By having xctest restart the process before each test case we
// ensure that the relevant MTRSetupPayload code paths correctly
// call chip::Platform::MemoryInit().
// Tests that are not specifically designed to test this should
// be added to MTRSetupPayloadTests to avoid this extra overhead.
return YES;
}

- (void)testSetupPayloadBasicQRCodeSerialize
{
Expand All @@ -49,6 +54,4 @@ - (void)testSetupPayloadBasicQRCodeSerialize
XCTAssertEqualObjects(qrCode, @"MT:-24J06.H14BK9C7R900");
}

// Make sure to not add any tests that involve parsing setup payloads to this
// file. Those should go in MTRSetupPayloadParserTests.m.
@end
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
//
// MTRSetupPayloadParserTests.m
// MTRQRCodeReaderTests
/**
*
* Copyright (c) 2020 Project CHIP Authors
Expand All @@ -17,20 +14,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// module headers
#import "MTRSetupPayload.h"

// additional includes
#import "MTRError.h"

// system dependencies
#import <Matter/Matter.h>
#import <XCTest/XCTest.h>

@interface MTRSetupPayloadParserTests : XCTestCase
@interface MTRSetupPayloadTests : XCTestCase

@end

@implementation MTRSetupPayloadParserTests
@implementation MTRSetupPayloadTests

- (void)testOnboardingPayloadParser_Manual_NoError
{
Expand Down Expand Up @@ -275,4 +267,80 @@ - (void)testSerialNumberRoundTrip
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);
[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]];

for (MTRSetupPayload * payload in payloads) {
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);

#if 0 // TODO: Encode / decode optional vendor data
// MTROptionalQRCodeInfo does not implement isEqual (yet)
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);
}
#endif

// 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);
}
}

@end
16 changes: 8 additions & 8 deletions src/darwin/Framework/Matter.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@
3CF134AF289D90FF0017A19E /* MTROperationalCertificateIssuer.h in Headers */ = {isa = PBXBuildFile; fileRef = 3CF134AE289D90FF0017A19E /* MTROperationalCertificateIssuer.h */; settings = {ATTRIBUTES = (Public, ); }; };
3D0C484B29DA4FA0006D811F /* MTRErrorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3D0C484A29DA4FA0006D811F /* MTRErrorTests.m */; };
3D3928D72BBCEA3D00CDEBB2 /* MTRAvailabilityTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3D3928D62BBCEA3D00CDEBB2 /* MTRAvailabilityTests.m */; settings = {COMPILER_FLAGS = "-UMTR_NO_AVAILABILITY -Wno-unguarded-availability-new"; }; };
3D4733AF2BDF1B80003DC19B /* MTRSetupPayloadTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3D4733AE2BDF1B80003DC19B /* MTRSetupPayloadTests.m */; };
3D69868529383096007314E7 /* com.csa.matter.plist in Copy Logging Preferences */ = {isa = PBXBuildFile; fileRef = 3D69868029382EF4007314E7 /* com.csa.matter.plist */; };
3D843711294977000070D20A /* NSStringSpanConversion.h in Headers */ = {isa = PBXBuildFile; fileRef = 3D84370E294977000070D20A /* NSStringSpanConversion.h */; };
3D843712294977000070D20A /* MTRCallbackBridgeBase.h in Headers */ = {isa = PBXBuildFile; fileRef = 3D84370F294977000070D20A /* MTRCallbackBridgeBase.h */; };
Expand Down Expand Up @@ -193,7 +194,7 @@
517BF3F3282B62CB00A8B7DB /* MTRCertificateTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 517BF3F2282B62CB00A8B7DB /* MTRCertificateTests.m */; };
518D3F832AA132DC008E0007 /* MTRTestPerControllerStorage.m in Sources */ = {isa = PBXBuildFile; fileRef = 518D3F812AA132DC008E0007 /* MTRTestPerControllerStorage.m */; };
518D3F852AA14006008E0007 /* MTRControllerAdvertisingTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 518D3F842AA14006008E0007 /* MTRControllerAdvertisingTests.m */; };
519498322A25581C00B3BABE /* MTRSetupPayloadSerializerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 519498312A25581C00B3BABE /* MTRSetupPayloadSerializerTests.m */; };
519498322A25581C00B3BABE /* MTRSetupPayloadInitializationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 519498312A25581C00B3BABE /* MTRSetupPayloadInitializationTests.m */; };
51A2F1322A00402A00F03298 /* MTRDataValueParserTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 51A2F1312A00402A00F03298 /* MTRDataValueParserTests.m */; };
51B22C1E2740CB0A008D5055 /* MTRStructsObjc.h in Headers */ = {isa = PBXBuildFile; fileRef = 51B22C1D2740CB0A008D5055 /* MTRStructsObjc.h */; settings = {ATTRIBUTES = (Public, ); }; };
51B22C222740CB1D008D5055 /* MTRCommandPayloadsObjc.h in Headers */ = {isa = PBXBuildFile; fileRef = 51B22C212740CB1D008D5055 /* MTRCommandPayloadsObjc.h */; settings = {ATTRIBUTES = (Public, ); }; };
Expand Down Expand Up @@ -309,7 +310,6 @@
B2E0D7B7245B0B5C003C5B48 /* MTRQRCodeSetupPayloadParser.mm in Sources */ = {isa = PBXBuildFile; fileRef = B2E0D7AE245B0B5C003C5B48 /* MTRQRCodeSetupPayloadParser.mm */; };
B2E0D7B8245B0B5C003C5B48 /* MTRSetupPayload.h in Headers */ = {isa = PBXBuildFile; fileRef = B2E0D7AF245B0B5C003C5B48 /* MTRSetupPayload.h */; settings = {ATTRIBUTES = (Public, ); }; };
B2E0D7B9245B0B5C003C5B48 /* MTRSetupPayload.mm in Sources */ = {isa = PBXBuildFile; fileRef = B2E0D7B0245B0B5C003C5B48 /* MTRSetupPayload.mm */; };
B2F53AF2245B0DCF0010745E /* MTRSetupPayloadParserTests.m in Sources */ = {isa = PBXBuildFile; fileRef = B2F53AF1245B0DCF0010745E /* MTRSetupPayloadParserTests.m */; };
B45373AA2A9FE73400807602 /* WebSocketServer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B45373A92A9FE73400807602 /* WebSocketServer.cpp */; };
B45373BD2A9FEA9100807602 /* service.c in Sources */ = {isa = PBXBuildFile; fileRef = B45373B22A9FEA9000807602 /* service.c */; settings = {COMPILER_FLAGS = "-Wno-error -Wno-unreachable-code -Wno-conversion -Wno-format-nonliteral"; }; };
B45373BE2A9FEA9100807602 /* network.c in Sources */ = {isa = PBXBuildFile; fileRef = B45373B32A9FEA9000807602 /* network.c */; settings = {COMPILER_FLAGS = "-Wno-error -Wno-unreachable-code -Wno-conversion -Wno-format-nonliteral"; }; };
Expand Down Expand Up @@ -498,6 +498,7 @@
3CF134AE289D90FF0017A19E /* MTROperationalCertificateIssuer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MTROperationalCertificateIssuer.h; sourceTree = "<group>"; };
3D0C484A29DA4FA0006D811F /* MTRErrorTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MTRErrorTests.m; sourceTree = "<group>"; };
3D3928D62BBCEA3D00CDEBB2 /* MTRAvailabilityTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MTRAvailabilityTests.m; sourceTree = "<group>"; };
3D4733AE2BDF1B80003DC19B /* MTRSetupPayloadTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MTRSetupPayloadTests.m; sourceTree = "<group>"; };
3D69868029382EF4007314E7 /* com.csa.matter.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = com.csa.matter.plist; sourceTree = "<group>"; };
3D84370E294977000070D20A /* NSStringSpanConversion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NSStringSpanConversion.h; sourceTree = "<group>"; };
3D84370F294977000070D20A /* MTRCallbackBridgeBase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MTRCallbackBridgeBase.h; sourceTree = "<group>"; };
Expand Down Expand Up @@ -601,7 +602,7 @@
518D3F812AA132DC008E0007 /* MTRTestPerControllerStorage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MTRTestPerControllerStorage.m; sourceTree = "<group>"; };
518D3F822AA132DC008E0007 /* MTRTestPerControllerStorage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MTRTestPerControllerStorage.h; sourceTree = "<group>"; };
518D3F842AA14006008E0007 /* MTRControllerAdvertisingTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MTRControllerAdvertisingTests.m; sourceTree = "<group>"; };
519498312A25581C00B3BABE /* MTRSetupPayloadSerializerTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MTRSetupPayloadSerializerTests.m; sourceTree = "<group>"; };
519498312A25581C00B3BABE /* MTRSetupPayloadInitializationTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MTRSetupPayloadInitializationTests.m; sourceTree = "<group>"; };
51A2F1312A00402A00F03298 /* MTRDataValueParserTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MTRDataValueParserTests.m; sourceTree = "<group>"; };
51B22C1D2740CB0A008D5055 /* MTRStructsObjc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MTRStructsObjc.h; sourceTree = "<group>"; };
51B22C212740CB1D008D5055 /* MTRCommandPayloadsObjc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MTRCommandPayloadsObjc.h; sourceTree = "<group>"; };
Expand Down Expand Up @@ -725,7 +726,6 @@
B2E0D7AE245B0B5C003C5B48 /* MTRQRCodeSetupPayloadParser.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MTRQRCodeSetupPayloadParser.mm; sourceTree = "<group>"; };
B2E0D7AF245B0B5C003C5B48 /* MTRSetupPayload.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MTRSetupPayload.h; sourceTree = "<group>"; };
B2E0D7B0245B0B5C003C5B48 /* MTRSetupPayload.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MTRSetupPayload.mm; sourceTree = "<group>"; };
B2F53AF1245B0DCF0010745E /* MTRSetupPayloadParserTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MTRSetupPayloadParserTests.m; sourceTree = "<group>"; };
B45373A92A9FE73400807602 /* WebSocketServer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WebSocketServer.cpp; sourceTree = "<group>"; };
B45373B22A9FEA9000807602 /* service.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = service.c; path = "repo/lib/core-net/service.c"; sourceTree = "<group>"; };
B45373B32A9FEA9000807602 /* network.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = network.c; path = "repo/lib/core-net/network.c"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1390,8 +1390,8 @@
51742B4D29CB6B88009974FE /* MTRPairingTests.m */,
51E95DF72A78110900A434F0 /* MTRPerControllerStorageTests.m */,
51D0B1292B61766F006E3511 /* MTRServerEndpointTests.m */,
B2F53AF1245B0DCF0010745E /* MTRSetupPayloadParserTests.m */,
519498312A25581C00B3BABE /* MTRSetupPayloadSerializerTests.m */,
3D4733AE2BDF1B80003DC19B /* MTRSetupPayloadTests.m */,
519498312A25581C00B3BABE /* MTRSetupPayloadInitializationTests.m */,
51E0FC0F2ACBBF230001E197 /* MTRSwiftDeviceTests.swift */,
5143851D2A65885500EDC8E6 /* MTRSwiftPairingTests.swift */,
997DED1926955D0200975E97 /* MTRThreadOperationalDatasetTests.mm */,
Expand Down Expand Up @@ -2000,13 +2000,13 @@
5AE6D4E427A99041001F2493 /* MTRDeviceTests.m in Sources */,
510CECA8297F72970064E0B3 /* MTROperationalCertificateIssuerTests.m in Sources */,
5A7947DE27BEC3F500434CF2 /* MTRXPCListenerSampleTests.m in Sources */,
B2F53AF2245B0DCF0010745E /* MTRSetupPayloadParserTests.m in Sources */,
3DFCB3292966684500332B35 /* MTRCertificateInfoTests.m in Sources */,
517BF3F3282B62CB00A8B7DB /* MTRCertificateTests.m in Sources */,
5142E39829D377F000A206F0 /* MTROTAProviderTests.m in Sources */,
51E0FC102ACBBF230001E197 /* MTRSwiftDeviceTests.swift in Sources */,
3D4733AF2BDF1B80003DC19B /* MTRSetupPayloadTests.m in Sources */,
51E24E73274E0DAC007CCF6E /* MTRErrorTestUtils.mm in Sources */,
519498322A25581C00B3BABE /* MTRSetupPayloadSerializerTests.m in Sources */,
519498322A25581C00B3BABE /* MTRSetupPayloadInitializationTests.m in Sources */,
51A2F1322A00402A00F03298 /* MTRDataValueParserTests.m in Sources */,
51E95DF82A78110900A434F0 /* MTRPerControllerStorageTests.m in Sources */,
51D9CB0B2BA37DCE0049D6DB /* MTRDSTOffsetTests.m in Sources */,
Expand Down

0 comments on commit 85d4114

Please sign in to comment.