Minor fix for autocreated object repeated fields and maps.

- If setting/clearing a repeated field/map that was objects, check the class
  before checking the autocreator.
- Just to be paranoid, don’t mutate within copy/mutableCopy for the autocreated
  classes to ensure there is less chance of issues if someone does something
  really crazy threading wise.
- Some more tests for the internal AutocreatedArray/AutocreatedDictionary
  classes to ensure things are working as expected.
- Add Xcode 8.2 to the full_mac_build.sh supported list.
This commit is contained in:
Thomas Van Lenten 2017-01-04 15:03:42 -05:00
parent 4cb113a91b
commit 988ffe0a78
11 changed files with 435 additions and 16 deletions

View File

@ -525,6 +525,7 @@ objectivec_EXTRA_DIST= \
objectivec/Tests/GPBDictionaryTests+String.m \
objectivec/Tests/GPBDictionaryTests+UInt32.m \
objectivec/Tests/GPBDictionaryTests+UInt64.m \
objectivec/Tests/GPBDictionaryTests.m \
objectivec/Tests/GPBDictionaryTests.pddm \
objectivec/Tests/GPBMessageTests+Merge.m \
objectivec/Tests/GPBMessageTests+Runtime.m \

View File

@ -265,6 +265,14 @@ if [[ "${DO_XCODE_IOS_TESTS}" == "yes" ]] ; then
-destination "platform=iOS Simulator,name=iPad Pro (9.7 inch),OS=10.1" # 64bit
)
;;
8.2* )
XCODEBUILD_TEST_BASE_IOS+=(
-destination "platform=iOS Simulator,name=iPhone 4s,OS=8.1" # 32bit
-destination "platform=iOS Simulator,name=iPhone 7,OS=10.2" # 64bit
-destination "platform=iOS Simulator,name=iPad 2,OS=8.1" # 32bit
-destination "platform=iOS Simulator,name=iPad Pro (9.7 inch),OS=10.2" # 64bit
)
;;
* )
echo "Time to update the simulator targets for Xcode ${XCODE_VERSION}"
exit 2

View File

@ -2519,14 +2519,14 @@ static BOOL ArrayDefault_IsValidValue(int32_t value) {
- (id)copyWithZone:(NSZone *)zone {
if (_array == nil) {
_array = [[NSMutableArray alloc] init];
return [[NSMutableArray allocWithZone:zone] init];
}
return [_array copyWithZone:zone];
}
- (id)mutableCopyWithZone:(NSZone *)zone {
if (_array == nil) {
_array = [[NSMutableArray alloc] init];
return [[NSMutableArray allocWithZone:zone] init];
}
return [_array mutableCopyWithZone:zone];
}

View File

@ -13579,22 +13579,26 @@ void GPBDictionaryReadEntry(id mapDictionary,
- (id)copyWithZone:(NSZone *)zone {
if (_dictionary == nil) {
_dictionary = [[NSMutableDictionary alloc] init];
return [[NSMutableDictionary allocWithZone:zone] init];
}
return [_dictionary copyWithZone:zone];
}
- (id)mutableCopyWithZone:(NSZone *)zone {
if (_dictionary == nil) {
_dictionary = [[NSMutableDictionary alloc] init];
return [[NSMutableDictionary allocWithZone:zone] init];
}
return [_dictionary mutableCopyWithZone:zone];
}
// Not really needed, but subscripting is likely common enough it doesn't hurt
// to ensure it goes directly to the real NSMutableDictionary.
- (id)objectForKeyedSubscript:(id)key {
return [_dictionary objectForKeyedSubscript:key];
}
// Not really needed, but subscripting is likely common enough it doesn't hurt
// to ensure it goes directly to the real NSMutableDictionary.
- (void)setObject:(id)obj forKeyedSubscript:(id<NSCopying>)key {
if (_dictionary == nil) {
_dictionary = [[NSMutableDictionary alloc] init];

View File

@ -1023,9 +1023,11 @@ static GPBUnknownFieldSet *GetOrMakeUnknownFields(GPBMessage *self) {
if (arrayOrMap) {
if (field.fieldType == GPBFieldTypeRepeated) {
if (GPBFieldDataTypeIsObject(field)) {
GPBAutocreatedArray *autoArray = arrayOrMap;
if (autoArray->_autocreator == self) {
autoArray->_autocreator = nil;
if ([arrayOrMap isKindOfClass:[GPBAutocreatedArray class]]) {
GPBAutocreatedArray *autoArray = arrayOrMap;
if (autoArray->_autocreator == self) {
autoArray->_autocreator = nil;
}
}
} else {
// Type doesn't matter, it is a GPB*Array.
@ -1037,9 +1039,11 @@ static GPBUnknownFieldSet *GetOrMakeUnknownFields(GPBMessage *self) {
} else {
if ((field.mapKeyDataType == GPBDataTypeString) &&
GPBFieldDataTypeIsObject(field)) {
GPBAutocreatedDictionary *autoDict = arrayOrMap;
if (autoDict->_autocreator == self) {
autoDict->_autocreator = nil;
if ([arrayOrMap isKindOfClass:[GPBAutocreatedDictionary class]]) {
GPBAutocreatedDictionary *autoDict = arrayOrMap;
if (autoDict->_autocreator == self) {
autoDict->_autocreator = nil;
}
}
} else {
// Type doesn't matter, it is a GPB*Dictionary.

View File

@ -408,9 +408,11 @@ void GPBSetRetainedObjectIvarWithFieldInternal(GPBMessage *self,
if (field.fieldType == GPBFieldTypeRepeated) {
// If the old array was autocreated by us, then clear it.
if (GPBDataTypeIsObject(fieldType)) {
GPBAutocreatedArray *autoArray = oldValue;
if (autoArray->_autocreator == self) {
autoArray->_autocreator = nil;
if ([oldValue isKindOfClass:[GPBAutocreatedArray class]]) {
GPBAutocreatedArray *autoArray = oldValue;
if (autoArray->_autocreator == self) {
autoArray->_autocreator = nil;
}
}
} else {
// Type doesn't matter, it is a GPB*Array.
@ -423,9 +425,11 @@ void GPBSetRetainedObjectIvarWithFieldInternal(GPBMessage *self,
// If the old map was autocreated by us, then clear it.
if ((field.mapKeyDataType == GPBDataTypeString) &&
GPBDataTypeIsObject(fieldType)) {
GPBAutocreatedDictionary *autoDict = oldValue;
if (autoDict->_autocreator == self) {
autoDict->_autocreator = nil;
if ([oldValue isKindOfClass:[GPBAutocreatedDictionary class]]) {
GPBAutocreatedDictionary *autoDict = oldValue;
if (autoDict->_autocreator == self) {
autoDict->_autocreator = nil;
}
}
} else {
// Type doesn't matter, it is a GPB*Dictionary.

View File

@ -57,6 +57,7 @@
F47476E51D21A524007C7B1A /* Duration.pbobjc.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B4248D41A92826400BC1EC6 /* Duration.pbobjc.m */; };
F47476E61D21A524007C7B1A /* Timestamp.pbobjc.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B4248D61A92826400BC1EC6 /* Timestamp.pbobjc.m */; };
F4B51B1E1BBC610700744318 /* GPBObjectiveCPlusPlusTest.mm in Sources */ = {isa = PBXBuildFile; fileRef = F4B51B1D1BBC610700744318 /* GPBObjectiveCPlusPlusTest.mm */; };
F4C4B9E41E1D976300D3B61D /* GPBDictionaryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F4C4B9E21E1D974F00D3B61D /* GPBDictionaryTests.m */; };
F4E675971B21D0000054530B /* Any.pbobjc.m in Sources */ = {isa = PBXBuildFile; fileRef = F4E675871B21D0000054530B /* Any.pbobjc.m */; };
F4E675991B21D0000054530B /* Api.pbobjc.m in Sources */ = {isa = PBXBuildFile; fileRef = F4E675891B21D0000054530B /* Api.pbobjc.m */; };
F4E6759B1B21D0000054530B /* Empty.pbobjc.m in Sources */ = {isa = PBXBuildFile; fileRef = F4E6758B1B21D0000054530B /* Empty.pbobjc.m */; };
@ -187,6 +188,7 @@
F4B6B8B21A9CCBDA00892426 /* GPBUnknownFieldSet_PackagePrivate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GPBUnknownFieldSet_PackagePrivate.h; sourceTree = "<group>"; };
F4B6B8B61A9CD1DE00892426 /* GPBExtensionInternals.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GPBExtensionInternals.h; sourceTree = "<group>"; };
F4B6B8B81A9CD1DE00892426 /* GPBRootObject_PackagePrivate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GPBRootObject_PackagePrivate.h; sourceTree = "<group>"; };
F4C4B9E21E1D974F00D3B61D /* GPBDictionaryTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GPBDictionaryTests.m; sourceTree = "<group>"; };
F4CF31701B162ED800BD9B06 /* unittest_objc_startup.proto */ = {isa = PBXFileReference; lastKnownFileType = text; path = unittest_objc_startup.proto; sourceTree = "<group>"; };
F4E675861B21D0000054530B /* Any.pbobjc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Any.pbobjc.h; path = google/protobuf/Any.pbobjc.h; sourceTree = "<group>"; };
F4E675871B21D0000054530B /* Any.pbobjc.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Any.pbobjc.m; path = google/protobuf/Any.pbobjc.m; sourceTree = "<group>"; };
@ -393,6 +395,7 @@
7461B69D0F94FDF800A0C422 /* GPBCodedOuputStreamTests.m */,
5102DABB1891A052002037B6 /* GPBConcurrencyTests.m */,
F4353D1C1AB8822D005A6198 /* GPBDescriptorTests.m */,
F4C4B9E21E1D974F00D3B61D /* GPBDictionaryTests.m */,
F4353D2C1AC06F10005A6198 /* GPBDictionaryTests.pddm */,
F4353D2D1AC06F10005A6198 /* GPBDictionaryTests+Bool.m */,
F4353D2E1AC06F10005A6198 /* GPBDictionaryTests+Int32.m */,
@ -677,6 +680,7 @@
F4353D371AC06F10005A6198 /* GPBDictionaryTests+String.m in Sources */,
F4353D381AC06F10005A6198 /* GPBDictionaryTests+UInt32.m in Sources */,
8BBEA4B7147C727D00C4ADB7 /* GPBUtilitiesTests.m in Sources */,
F4C4B9E41E1D976300D3B61D /* GPBDictionaryTests.m in Sources */,
8BBEA4B8147C727D00C4ADB7 /* GPBWireFormatTests.m in Sources */,
8BD3981F14BE59D70081D629 /* GPBUnittestProtos.m in Sources */,
8B8B615D17DF7056002EE618 /* GPBARCUnittestProtos.m in Sources */,

View File

@ -65,6 +65,7 @@
F47476E91D21A537007C7B1A /* Duration.pbobjc.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B4248DE1A929C7D00BC1EC6 /* Duration.pbobjc.m */; };
F47476EA1D21A537007C7B1A /* Timestamp.pbobjc.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B4248E01A929C7D00BC1EC6 /* Timestamp.pbobjc.m */; };
F4B51B1C1BBC5C7100744318 /* GPBObjectiveCPlusPlusTest.mm in Sources */ = {isa = PBXBuildFile; fileRef = F4B51B1B1BBC5C7100744318 /* GPBObjectiveCPlusPlusTest.mm */; };
F4C4B9E71E1D97BF00D3B61D /* GPBDictionaryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F4C4B9E51E1D97BB00D3B61D /* GPBDictionaryTests.m */; };
F4E675D01B21D1620054530B /* Any.pbobjc.m in Sources */ = {isa = PBXBuildFile; fileRef = F4E675B71B21D1440054530B /* Any.pbobjc.m */; };
F4E675D11B21D1620054530B /* Api.pbobjc.m in Sources */ = {isa = PBXBuildFile; fileRef = F4E675B91B21D1440054530B /* Api.pbobjc.m */; };
F4E675D21B21D1620054530B /* Empty.pbobjc.m in Sources */ = {isa = PBXBuildFile; fileRef = F4E675BC1B21D1440054530B /* Empty.pbobjc.m */; };
@ -209,6 +210,7 @@
F4B6B8B11A9CCBBB00892426 /* GPBUnknownFieldSet_PackagePrivate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GPBUnknownFieldSet_PackagePrivate.h; sourceTree = "<group>"; };
F4B6B8B31A9CD1C600892426 /* GPBExtensionInternals.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GPBExtensionInternals.h; sourceTree = "<group>"; };
F4B6B8B51A9CD1C600892426 /* GPBRootObject_PackagePrivate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GPBRootObject_PackagePrivate.h; sourceTree = "<group>"; };
F4C4B9E51E1D97BB00D3B61D /* GPBDictionaryTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GPBDictionaryTests.m; sourceTree = "<group>"; };
F4CF31711B162EF500BD9B06 /* unittest_objc_startup.proto */ = {isa = PBXFileReference; lastKnownFileType = text; path = unittest_objc_startup.proto; sourceTree = "<group>"; };
F4E675B61B21D1440054530B /* Any.pbobjc.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Any.pbobjc.h; path = google/protobuf/Any.pbobjc.h; sourceTree = "<group>"; };
F4E675B71B21D1440054530B /* Any.pbobjc.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = Any.pbobjc.m; path = google/protobuf/Any.pbobjc.m; sourceTree = "<group>"; };
@ -431,6 +433,7 @@
7461B69D0F94FDF800A0C422 /* GPBCodedOuputStreamTests.m */,
5102DABB1891A052002037B6 /* GPBConcurrencyTests.m */,
F4353D1E1AB88243005A6198 /* GPBDescriptorTests.m */,
F4C4B9E51E1D97BB00D3B61D /* GPBDictionaryTests.m */,
F4353D3A1AC06F31005A6198 /* GPBDictionaryTests.pddm */,
F4353D3B1AC06F31005A6198 /* GPBDictionaryTests+Bool.m */,
F4353D3C1AC06F31005A6198 /* GPBDictionaryTests+Int32.m */,
@ -773,6 +776,7 @@
F4353D451AC06F31005A6198 /* GPBDictionaryTests+String.m in Sources */,
F4353D461AC06F31005A6198 /* GPBDictionaryTests+UInt32.m in Sources */,
8BBEA4B7147C727D00C4ADB7 /* GPBUtilitiesTests.m in Sources */,
F4C4B9E71E1D97BF00D3B61D /* GPBDictionaryTests.m in Sources */,
8BBEA4B8147C727D00C4ADB7 /* GPBWireFormatTests.m in Sources */,
8BD3981F14BE59D70081D629 /* GPBUnittestProtos.m in Sources */,
8B8B615D17DF7056002EE618 /* GPBARCUnittestProtos.m in Sources */,

View File

@ -32,6 +32,7 @@
#import <XCTest/XCTest.h>
#import "GPBArray.h"
#import "GPBArray_PackagePrivate.h"
#import "GPBTestUtilities.h"
@ -3436,3 +3437,175 @@ static BOOL TestingEnum_IsValidValue2(int32_t value) {
}
@end
#pragma mark - GPBAutocreatedArray Tests
// These are hand written tests to double check some behaviors of the
// GPBAutocreatedArray.
// NOTE: GPBAutocreatedArray is private to the library, users of the library
// should never have to directly deal with this class.
@interface GPBAutocreatedArrayTests : XCTestCase
@end
@implementation GPBAutocreatedArrayTests
- (void)testEquality {
GPBAutocreatedArray *array = [[GPBAutocreatedArray alloc] init];
XCTAssertTrue([array isEqual:@[]]);
XCTAssertTrue([array isEqualToArray:@[]]);
XCTAssertFalse([array isEqual:@[ @"foo" ]]);
XCTAssertFalse([array isEqualToArray:@[ @"foo" ]]);
[array addObject:@"foo"];
XCTAssertFalse([array isEqual:@[]]);
XCTAssertFalse([array isEqualToArray:@[]]);
XCTAssertTrue([array isEqual:@[ @"foo" ]]);
XCTAssertTrue([array isEqualToArray:@[ @"foo" ]]);
XCTAssertFalse([array isEqual:@[ @"bar" ]]);
XCTAssertFalse([array isEqualToArray:@[ @"bar" ]]);
GPBAutocreatedArray *array2 = [[GPBAutocreatedArray alloc] init];
XCTAssertFalse([array isEqual:array2]);
XCTAssertFalse([array isEqualToArray:array2]);
[array2 addObject:@"bar"];
XCTAssertFalse([array isEqual:array2]);
XCTAssertFalse([array isEqualToArray:array2]);
[array2 replaceObjectAtIndex:0 withObject:@"foo"];
XCTAssertTrue([array isEqual:array2]);
XCTAssertTrue([array isEqualToArray:array2]);
[array2 release];
[array release];
}
- (void)testCopy {
{
GPBAutocreatedArray *array = [[GPBAutocreatedArray alloc] init];
NSArray *cpy = [array copy];
XCTAssertTrue(cpy != array); // Ptr compare
XCTAssertTrue([cpy isKindOfClass:[NSArray class]]);
XCTAssertFalse([cpy isKindOfClass:[GPBAutocreatedArray class]]);
XCTAssertEqual(cpy.count, (NSUInteger)0);
NSArray *cpy2 = [array copy];
XCTAssertTrue(cpy2 != array); // Ptr compare
// Can't compare cpy and cpy2 because NSArray has a singleton empty
// array it uses, so the ptrs are the same.
XCTAssertTrue([cpy2 isKindOfClass:[NSArray class]]);
XCTAssertFalse([cpy2 isKindOfClass:[GPBAutocreatedArray class]]);
XCTAssertEqual(cpy2.count, (NSUInteger)0);
[cpy2 release];
[cpy release];
[array release];
}
{
GPBAutocreatedArray *array = [[GPBAutocreatedArray alloc] init];
NSMutableArray *cpy = [array mutableCopy];
XCTAssertTrue(cpy != array); // Ptr compare
XCTAssertTrue([cpy isKindOfClass:[NSMutableArray class]]);
XCTAssertFalse([cpy isKindOfClass:[GPBAutocreatedArray class]]);
XCTAssertEqual(cpy.count, (NSUInteger)0);
NSMutableArray *cpy2 = [array mutableCopy];
XCTAssertTrue(cpy2 != array); // Ptr compare
XCTAssertTrue(cpy2 != cpy); // Ptr compare
XCTAssertTrue([cpy2 isKindOfClass:[NSMutableArray class]]);
XCTAssertFalse([cpy2 isKindOfClass:[GPBAutocreatedArray class]]);
XCTAssertEqual(cpy2.count, (NSUInteger)0);
[cpy2 release];
[cpy release];
[array release];
}
{
GPBAutocreatedArray *array = [[GPBAutocreatedArray alloc] init];
[array addObject:@"foo"];
[array addObject:@"bar"];
NSArray *cpy = [array copy];
XCTAssertTrue(cpy != array); // Ptr compare
XCTAssertTrue([cpy isKindOfClass:[NSArray class]]);
XCTAssertFalse([cpy isKindOfClass:[GPBAutocreatedArray class]]);
XCTAssertEqual(cpy.count, (NSUInteger)2);
XCTAssertEqualObjects(cpy[0], @"foo");
XCTAssertEqualObjects(cpy[1], @"bar");
NSArray *cpy2 = [array copy];
XCTAssertTrue(cpy2 != array); // Ptr compare
XCTAssertTrue(cpy2 != cpy); // Ptr compare
XCTAssertTrue([cpy2 isKindOfClass:[NSArray class]]);
XCTAssertFalse([cpy2 isKindOfClass:[GPBAutocreatedArray class]]);
XCTAssertEqual(cpy2.count, (NSUInteger)2);
XCTAssertEqualObjects(cpy2[0], @"foo");
XCTAssertEqualObjects(cpy2[1], @"bar");
[cpy2 release];
[cpy release];
[array release];
}
{
GPBAutocreatedArray *array = [[GPBAutocreatedArray alloc] init];
[array addObject:@"foo"];
[array addObject:@"bar"];
NSMutableArray *cpy = [array mutableCopy];
XCTAssertTrue(cpy != array); // Ptr compare
XCTAssertTrue([cpy isKindOfClass:[NSArray class]]);
XCTAssertFalse([cpy isKindOfClass:[GPBAutocreatedArray class]]);
XCTAssertEqual(cpy.count, (NSUInteger)2);
XCTAssertEqualObjects(cpy[0], @"foo");
XCTAssertEqualObjects(cpy[1], @"bar");
NSMutableArray *cpy2 = [array mutableCopy];
XCTAssertTrue(cpy2 != array); // Ptr compare
XCTAssertTrue(cpy2 != cpy); // Ptr compare
XCTAssertTrue([cpy2 isKindOfClass:[NSArray class]]);
XCTAssertFalse([cpy2 isKindOfClass:[GPBAutocreatedArray class]]);
XCTAssertEqual(cpy2.count, (NSUInteger)2);
XCTAssertEqualObjects(cpy2[0], @"foo");
XCTAssertEqualObjects(cpy2[1], @"bar");
[cpy2 release];
[cpy release];
[array release];
}
}
- (void)testIndexedSubscriptSupport {
// The base NSArray/NSMutableArray behaviors for *IndexedSubscript methods
// should still work via the methods that one has to override to make an
// NSMutableArray subclass. i.e. - this should "just work" and if these
// crash/fail, then something is wrong in how NSMutableArray is subclassed.
GPBAutocreatedArray *array = [[GPBAutocreatedArray alloc] init];
[array addObject:@"foo"];
[array addObject:@"bar"];
XCTAssertEqual(array.count, (NSUInteger)2);
XCTAssertEqualObjects(array[0], @"foo");
XCTAssertEqualObjects(array[1], @"bar");
array[0] = @"foo2";
array[2] = @"baz";
XCTAssertEqual(array.count, (NSUInteger)3);
XCTAssertEqualObjects(array[0], @"foo2");
XCTAssertEqualObjects(array[1], @"bar");
XCTAssertEqualObjects(array[2], @"baz");
[array release];
}
@end

View File

@ -0,0 +1,186 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2017 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#import <Foundation/Foundation.h>
#import <XCTest/XCTest.h>
#import "GPBDictionary.h"
#import "GPBDictionary_PackagePrivate.h"
#import "GPBTestUtilities.h"
#pragma mark - GPBAutocreatedDictionary Tests
// These are hand written tests to double check some behaviors of the
// GPBAutocreatedDictionary. The GPBDictionary+[type]Tests files are generate
// tests.
// NOTE: GPBAutocreatedDictionary is private to the library, users of the
// library should never have to directly deal with this class.
@interface GPBAutocreatedDictionaryTests : XCTestCase
@end
@implementation GPBAutocreatedDictionaryTests
- (void)testEquality {
GPBAutocreatedDictionary *dict = [[GPBAutocreatedDictionary alloc] init];
XCTAssertTrue([dict isEqual:@{}]);
XCTAssertTrue([dict isEqualToDictionary:@{}]);
XCTAssertFalse([dict isEqual:@{ @"foo" : @"bar" }]);
XCTAssertFalse([dict isEqualToDictionary:@{ @"foo" : @"bar" }]);
[dict setObject:@"bar" forKey:@"foo"];
XCTAssertFalse([dict isEqual:@{}]);
XCTAssertFalse([dict isEqualToDictionary:@{}]);
XCTAssertTrue([dict isEqual:@{ @"foo" : @"bar" }]);
XCTAssertTrue([dict isEqualToDictionary:@{ @"foo" : @"bar" }]);
XCTAssertFalse([dict isEqual:@{ @"bar" : @"baz" }]);
XCTAssertFalse([dict isEqualToDictionary:@{ @"bar" : @"baz" }]);
GPBAutocreatedDictionary *dict2 = [[GPBAutocreatedDictionary alloc] init];
XCTAssertFalse([dict isEqual:dict2]);
XCTAssertFalse([dict isEqualToDictionary:dict2]);
[dict2 setObject:@"mumble" forKey:@"foo"];
XCTAssertFalse([dict isEqual:dict2]);
XCTAssertFalse([dict isEqualToDictionary:dict2]);
[dict2 setObject:@"bar" forKey:@"foo"];
XCTAssertTrue([dict isEqual:dict2]);
XCTAssertTrue([dict isEqualToDictionary:dict2]);
[dict2 release];
[dict release];
}
- (void)testCopy {
{
GPBAutocreatedDictionary *dict = [[GPBAutocreatedDictionary alloc] init];
NSDictionary *cpy = [dict copy];
XCTAssertTrue(cpy != dict); // Ptr compare
XCTAssertTrue([cpy isKindOfClass:[NSDictionary class]]);
XCTAssertFalse([cpy isKindOfClass:[GPBAutocreatedDictionary class]]);
XCTAssertEqual(cpy.count, (NSUInteger)0);
NSDictionary *cpy2 = [dict copy];
XCTAssertTrue(cpy2 != dict); // Ptr compare
XCTAssertTrue(cpy2 != cpy); // Ptr compare
XCTAssertTrue([cpy2 isKindOfClass:[NSDictionary class]]);
XCTAssertFalse([cpy2 isKindOfClass:[GPBAutocreatedDictionary class]]);
XCTAssertEqual(cpy2.count, (NSUInteger)0);
[cpy2 release];
[cpy release];
[dict release];
}
{
GPBAutocreatedDictionary *dict = [[GPBAutocreatedDictionary alloc] init];
NSMutableDictionary *cpy = [dict mutableCopy];
XCTAssertTrue(cpy != dict); // Ptr compare
XCTAssertTrue([cpy isKindOfClass:[NSMutableDictionary class]]);
XCTAssertFalse([cpy isKindOfClass:[GPBAutocreatedDictionary class]]);
XCTAssertEqual(cpy.count, (NSUInteger)0);
NSMutableDictionary *cpy2 = [dict mutableCopy];
XCTAssertTrue(cpy2 != dict); // Ptr compare
XCTAssertTrue(cpy2 != cpy); // Ptr compare
XCTAssertTrue([cpy2 isKindOfClass:[NSMutableDictionary class]]);
XCTAssertFalse([cpy2 isKindOfClass:[GPBAutocreatedDictionary class]]);
XCTAssertEqual(cpy2.count, (NSUInteger)0);
[cpy2 release];
[cpy release];
[dict release];
}
{
GPBAutocreatedDictionary *dict = [[GPBAutocreatedDictionary alloc] init];
dict[@"foo"] = @"bar";
dict[@"baz"] = @"mumble";
NSDictionary *cpy = [dict copy];
XCTAssertTrue(cpy != dict); // Ptr compare
XCTAssertTrue([cpy isKindOfClass:[NSDictionary class]]);
XCTAssertFalse([cpy isKindOfClass:[GPBAutocreatedDictionary class]]);
XCTAssertEqual(cpy.count, (NSUInteger)2);
XCTAssertEqualObjects(cpy[@"foo"], @"bar");
XCTAssertEqualObjects(cpy[@"baz"], @"mumble");
NSDictionary *cpy2 = [dict copy];
XCTAssertTrue(cpy2 != dict); // Ptr compare
XCTAssertTrue(cpy2 != cpy); // Ptr compare
XCTAssertTrue([cpy2 isKindOfClass:[NSDictionary class]]);
XCTAssertFalse([cpy2 isKindOfClass:[GPBAutocreatedDictionary class]]);
XCTAssertEqual(cpy2.count, (NSUInteger)2);
XCTAssertEqualObjects(cpy2[@"foo"], @"bar");
XCTAssertEqualObjects(cpy2[@"baz"], @"mumble");
[cpy2 release];
[cpy release];
[dict release];
}
{
GPBAutocreatedDictionary *dict = [[GPBAutocreatedDictionary alloc] init];
dict[@"foo"] = @"bar";
dict[@"baz"] = @"mumble";
NSMutableDictionary *cpy = [dict mutableCopy];
XCTAssertTrue(cpy != dict); // Ptr compare
XCTAssertTrue([cpy isKindOfClass:[NSMutableDictionary class]]);
XCTAssertFalse([cpy isKindOfClass:[GPBAutocreatedDictionary class]]);
XCTAssertEqual(cpy.count, (NSUInteger)2);
XCTAssertEqualObjects(cpy[@"foo"], @"bar");
XCTAssertEqualObjects(cpy[@"baz"], @"mumble");
NSMutableDictionary *cpy2 = [dict mutableCopy];
XCTAssertTrue(cpy2 != dict); // Ptr compare
XCTAssertTrue(cpy2 != cpy); // Ptr compare
XCTAssertTrue([cpy2 isKindOfClass:[NSMutableDictionary class]]);
XCTAssertFalse([cpy2 isKindOfClass:[GPBAutocreatedDictionary class]]);
XCTAssertEqual(cpy2.count, (NSUInteger)2);
XCTAssertEqualObjects(cpy2[@"foo"], @"bar");
XCTAssertEqualObjects(cpy2[@"baz"], @"mumble");
[cpy2 release];
[cpy release];
[dict release];
}
}
@end

View File

@ -1082,6 +1082,20 @@
[repeatedStringArray release];
}
- (void)testSetOverAutocreatedArrayAndSetAgain {
// Ensure when dealing with replacing an array it is handled being either
// an autocreated one or a straight NSArray.
// The real test here is that nothing crashes while doing the work.
TestAllTypes *message = [TestAllTypes message];
[message.repeatedStringArray addObject:@"foo"];
XCTAssertEqual(message.repeatedStringArray_Count, (NSUInteger)1);
message.repeatedStringArray = [NSMutableArray arrayWithObjects:@"bar", @"bar2", nil];
XCTAssertEqual(message.repeatedStringArray_Count, (NSUInteger)2);
message.repeatedStringArray = [NSMutableArray arrayWithObject:@"baz"];
XCTAssertEqual(message.repeatedStringArray_Count, (NSUInteger)1);
}
- (void)testReplaceAutocreatedArray {
// Replacing array should orphan the old one and cause its creator to become
// visible.
@ -1281,6 +1295,23 @@
[strToStr release];
}
- (void)testSetOverAutocreatedMapAndSetAgain {
// Ensure when dealing with replacing a map it is handled being either
// an autocreated one or a straight NSDictionary.
// The real test here is that nothing crashes while doing the work.
TestRecursiveMessageWithRepeatedField *message =
[TestRecursiveMessageWithRepeatedField message];
message.strToStr[@"foo"] = @"bar";
XCTAssertEqual(message.strToStr_Count, (NSUInteger)1);
message.strToStr =
[NSMutableDictionary dictionaryWithObjectsAndKeys:@"bar", @"key1", @"baz", @"key2", nil];
XCTAssertEqual(message.strToStr_Count, (NSUInteger)2);
message.strToStr =
[NSMutableDictionary dictionaryWithObject:@"baz" forKey:@"mumble"];
XCTAssertEqual(message.strToStr_Count, (NSUInteger)1);
}
- (void)testReplaceAutocreatedMap {
// Replacing map should orphan the old one and cause its creator to become
// visible.