aboutsummaryrefslogtreecommitdiff
path: root/objectivec/Tests
diff options
context:
space:
mode:
Diffstat (limited to 'objectivec/Tests')
-rw-r--r--objectivec/Tests/GPBCodedOuputStreamTests.m86
-rw-r--r--objectivec/Tests/GPBDescriptorTests.m29
-rw-r--r--objectivec/Tests/GPBMessageTests+Runtime.m399
-rw-r--r--objectivec/Tests/GPBUnittestProtos.m9
-rw-r--r--objectivec/Tests/GPBUnittestProtos2.m34
-rw-r--r--objectivec/Tests/GPBWellKnownTypesTest.m56
-rw-r--r--objectivec/Tests/unittest_cycle.proto6
-rw-r--r--objectivec/Tests/unittest_extension_chain_a.proto51
-rw-r--r--objectivec/Tests/unittest_extension_chain_b.proto47
-rw-r--r--objectivec/Tests/unittest_extension_chain_c.proto45
-rw-r--r--objectivec/Tests/unittest_extension_chain_d.proto49
-rw-r--r--objectivec/Tests/unittest_extension_chain_e.proto40
-rw-r--r--objectivec/Tests/unittest_extension_chain_f.proto44
-rw-r--r--objectivec/Tests/unittest_extension_chain_g.proto41
-rw-r--r--objectivec/Tests/unittest_objc.proto9
15 files changed, 934 insertions, 11 deletions
diff --git a/objectivec/Tests/GPBCodedOuputStreamTests.m b/objectivec/Tests/GPBCodedOuputStreamTests.m
index 0723b645..2ad326be 100644
--- a/objectivec/Tests/GPBCodedOuputStreamTests.m
+++ b/objectivec/Tests/GPBCodedOuputStreamTests.m
@@ -193,6 +193,32 @@
}
}
+- (void)assertWriteStringNoTag:(NSData*)data
+ value:(NSString *)value
+ context:(NSString *)contextMessage {
+ NSOutputStream* rawOutput = [NSOutputStream outputStreamToMemory];
+ GPBCodedOutputStream* output =
+ [GPBCodedOutputStream streamWithOutputStream:rawOutput];
+ [output writeStringNoTag:value];
+ [output flush];
+
+ NSData* actual =
+ [rawOutput propertyForKey:NSStreamDataWrittenToMemoryStreamKey];
+ XCTAssertEqualObjects(data, actual, @"%@", contextMessage);
+
+ // Try different block sizes.
+ for (int blockSize = 1; blockSize <= 16; blockSize *= 2) {
+ rawOutput = [NSOutputStream outputStreamToMemory];
+ output = [GPBCodedOutputStream streamWithOutputStream:rawOutput
+ bufferSize:blockSize];
+ [output writeStringNoTag:value];
+ [output flush];
+
+ actual = [rawOutput propertyForKey:NSStreamDataWrittenToMemoryStreamKey];
+ XCTAssertEqualObjects(data, actual, @"%@", contextMessage);
+ }
+}
+
- (void)testWriteVarint1 {
[self assertWriteVarint:bytes(0x00) value:0];
}
@@ -337,4 +363,64 @@
XCTAssertEqualObjects(rawBytes, goldenData);
}
+- (void)testCFStringGetCStringPtrAndStringsWithNullChars {
+ // This test exists to verify that CFStrings with embedded NULLs still expose
+ // their raw buffer if they are backed by UTF8 storage. If this fails, the
+ // quick/direct access paths in GPBCodedOutputStream that depend on
+ // CFStringGetCStringPtr need to be re-evalutated (maybe just removed).
+ // And yes, we do get NULLs in strings from some servers.
+
+ char zeroTest[] = "\0Test\0String";
+ // Note: there is a \0 at the end of this since it is a c-string.
+ NSString *asNSString = [[NSString alloc] initWithBytes:zeroTest
+ length:sizeof(zeroTest)
+ encoding:NSUTF8StringEncoding];
+ const char *cString =
+ CFStringGetCStringPtr((CFStringRef)asNSString, kCFStringEncodingUTF8);
+ XCTAssertTrue(cString != NULL);
+ // Again, if the above assert fails, then it means NSString no longer exposes
+ // the raw utf8 storage of a string created from utf8 input, so the code using
+ // CFStringGetCStringPtr in GPBCodedOutputStream will still work (it will take
+ // a different code path); but the optimizations for when
+ // CFStringGetCStringPtr does work could possibly go away.
+
+ XCTAssertEqual(sizeof(zeroTest),
+ [asNSString lengthOfBytesUsingEncoding:NSUTF8StringEncoding]);
+ XCTAssertTrue(0 == memcmp(cString, zeroTest, sizeof(zeroTest)));
+ [asNSString release];
+}
+
+- (void)testWriteStringsWithZeroChar {
+ // Unicode allows `\0` as a character, and NSString is a class cluster, so
+ // there are a few different classes that could end up beind a given string.
+ // Historically, we've seen differences based on constant strings in code and
+ // strings built via the NSString apis. So this round trips them to ensure
+ // they are acting as expected.
+
+ NSArray<NSString *> *strs = @[
+ @"\0at start",
+ @"in\0middle",
+ @"at end\0",
+ ];
+ int i = 0;
+ for (NSString *str in strs) {
+ NSData *asUTF8 = [str dataUsingEncoding:NSUTF8StringEncoding];
+ NSMutableData *expected = [NSMutableData data];
+ uint8_t lengthByte = (uint8_t)asUTF8.length;
+ [expected appendBytes:&lengthByte length:1];
+ [expected appendData:asUTF8];
+
+ NSString *context = [NSString stringWithFormat:@"Loop %d - Literal", i];
+ [self assertWriteStringNoTag:expected value:str context:context];
+
+ // Force a new string to be built which gets a different class from the
+ // NSString class cluster than the literal did.
+ NSString *str2 = [NSString stringWithFormat:@"%@", str];
+ context = [NSString stringWithFormat:@"Loop %d - Built", i];
+ [self assertWriteStringNoTag:expected value:str2 context:context];
+
+ ++i;
+ }
+}
+
@end
diff --git a/objectivec/Tests/GPBDescriptorTests.m b/objectivec/Tests/GPBDescriptorTests.m
index 74e3172b..1e1c3de8 100644
--- a/objectivec/Tests/GPBDescriptorTests.m
+++ b/objectivec/Tests/GPBDescriptorTests.m
@@ -34,12 +34,41 @@
#import "GPBDescriptor.h"
#import "google/protobuf/Unittest.pbobjc.h"
+#import "google/protobuf/UnittestObjc.pbobjc.h"
+#import "google/protobuf/Descriptor.pbobjc.h"
@interface DescriptorTests : GPBTestCase
@end
@implementation DescriptorTests
+- (void)testDescriptor_containingType {
+ GPBDescriptor *testAllTypesDesc = [TestAllTypes descriptor];
+ GPBDescriptor *nestedMessageDesc = [TestAllTypes_NestedMessage descriptor];
+ XCTAssertNil(testAllTypesDesc.containingType);
+ XCTAssertNotNil(nestedMessageDesc.containingType);
+ XCTAssertEqual(nestedMessageDesc.containingType, testAllTypesDesc); // Ptr comparison
+}
+
+- (void)testDescriptor_fullName {
+ GPBDescriptor *testAllTypesDesc = [TestAllTypes descriptor];
+ XCTAssertEqualObjects(testAllTypesDesc.fullName, @"protobuf_unittest.TestAllTypes");
+ GPBDescriptor *nestedMessageDesc = [TestAllTypes_NestedMessage descriptor];
+ XCTAssertEqualObjects(nestedMessageDesc.fullName, @"protobuf_unittest.TestAllTypes.NestedMessage");
+
+ // Prefixes removed.
+ GPBDescriptor *descDesc = [GPBDescriptorProto descriptor];
+ XCTAssertEqualObjects(descDesc.fullName, @"google.protobuf.DescriptorProto");
+ GPBDescriptor *descExtRngDesc = [GPBDescriptorProto_ExtensionRange descriptor];
+ XCTAssertEqualObjects(descExtRngDesc.fullName, @"google.protobuf.DescriptorProto.ExtensionRange");
+
+ // Things that get "_Class" added.
+ GPBDescriptor *pointDesc = [Point_Class descriptor];
+ XCTAssertEqualObjects(pointDesc.fullName, @"protobuf_unittest.Point");
+ GPBDescriptor *pointRectDesc = [Point_Rect descriptor];
+ XCTAssertEqualObjects(pointRectDesc.fullName, @"protobuf_unittest.Point.Rect");
+}
+
- (void)testFieldDescriptor {
GPBDescriptor *descriptor = [TestAllTypes descriptor];
diff --git a/objectivec/Tests/GPBMessageTests+Runtime.m b/objectivec/Tests/GPBMessageTests+Runtime.m
index 1520381b..0058311b 100644
--- a/objectivec/Tests/GPBMessageTests+Runtime.m
+++ b/objectivec/Tests/GPBMessageTests+Runtime.m
@@ -36,6 +36,7 @@
#import "google/protobuf/MapUnittest.pbobjc.h"
#import "google/protobuf/Unittest.pbobjc.h"
+#import "google/protobuf/UnittestCycle.pbobjc.h"
#import "google/protobuf/UnittestObjcStartup.pbobjc.h"
#import "google/protobuf/UnittestRuntimeProto2.pbobjc.h"
#import "google/protobuf/UnittestRuntimeProto3.pbobjc.h"
@@ -49,11 +50,24 @@
// specific.
- (void)testStartupOrdering {
- // Just have to create a message. Nothing else uses the classes from
- // this file, so the first selector invoked on the class will initialize
- // it, which also initializes the root.
+ // Message class/Root class initialization is a little tricky, so these just
+ // create some possible patterns that can be a problem. The messages don't
+ // have to be exercised, just creating them is enough to test. If there
+ // is a problem, the runtime should assert or hang.
+ //
+ // Note: the messages from these proto files should not be used in any other
+ // tests, that way when they are referenced here it will be the first use and
+ // initialization will take place now.
+
TestObjCStartupMessage *message = [TestObjCStartupMessage message];
XCTAssertNotNil(message);
+
+ CycleBaz *baz = [CycleBaz message];
+ CycleBar *bar = [CycleBar message];
+ CycleFoo *foo = [CycleFoo message];
+ XCTAssertNotNil(baz);
+ XCTAssertNotNil(bar);
+ XCTAssertNotNil(foo);
}
- (void)testProto2HasMethodSupport {
@@ -326,6 +340,17 @@
//% [msg release];
//% }
//%
+//%PDDM-DEFINE PROTO2_TEST_CLEAR_FIELD_WITH_NIL(FIELD, VALUE)
+//% { // optional##FIELD
+//% Message2 *msg = [[Message2 alloc] init];
+//% XCTAssertFalse(GPBMessageHasFieldNumberSet(msg, Message2_FieldNumber_Optional##FIELD));
+//% msg.optional##FIELD = VALUE;
+//% XCTAssertTrue(GPBMessageHasFieldNumberSet(msg, Message2_FieldNumber_Optional##FIELD));
+//% msg.optional##FIELD = nil;
+//% XCTAssertFalse(GPBMessageHasFieldNumberSet(msg, Message2_FieldNumber_Optional##FIELD));
+//% [msg release];
+//% }
+//%
//%PDDM-DEFINE PROTO2_TEST_HAS_FIELDS()
//%PROTO2_TEST_HAS_FIELD(Int32, 1, 0)
//%PROTO2_TEST_HAS_FIELD(Int64, 1, 0)
@@ -347,6 +372,14 @@
//% //
//%
//%PROTO2_TEST_HAS_FIELD(Enum, Message2_Enum_Bar, Message2_Enum_Foo)
+//% //
+//% // Nil can also be used to clear strings, bytes, groups, and messages.
+//% //
+//%
+//%PROTO2_TEST_CLEAR_FIELD_WITH_NIL(String, @"foo")
+//%PROTO2_TEST_CLEAR_FIELD_WITH_NIL(Bytes, [@"foo" dataUsingEncoding:NSUTF8StringEncoding])
+//%PROTO2_TEST_CLEAR_FIELD_WITH_NIL(Group, [Message2_OptionalGroup message])
+//%PROTO2_TEST_CLEAR_FIELD_WITH_NIL(Message, [Message2 message])
//%PDDM-EXPAND PROTO2_TEST_HAS_FIELDS()
// This block of code is generated, do not edit it directly.
@@ -658,13 +691,57 @@
[msg release];
}
+ //
+ // Nil can also be used to clear strings, bytes, groups, and messages.
+ //
+
+ { // optionalString
+ Message2 *msg = [[Message2 alloc] init];
+ XCTAssertFalse(GPBMessageHasFieldNumberSet(msg, Message2_FieldNumber_OptionalString));
+ msg.optionalString = @"foo";
+ XCTAssertTrue(GPBMessageHasFieldNumberSet(msg, Message2_FieldNumber_OptionalString));
+ msg.optionalString = nil;
+ XCTAssertFalse(GPBMessageHasFieldNumberSet(msg, Message2_FieldNumber_OptionalString));
+ [msg release];
+ }
+
+ { // optionalBytes
+ Message2 *msg = [[Message2 alloc] init];
+ XCTAssertFalse(GPBMessageHasFieldNumberSet(msg, Message2_FieldNumber_OptionalBytes));
+ msg.optionalBytes = [@"foo" dataUsingEncoding:NSUTF8StringEncoding];
+ XCTAssertTrue(GPBMessageHasFieldNumberSet(msg, Message2_FieldNumber_OptionalBytes));
+ msg.optionalBytes = nil;
+ XCTAssertFalse(GPBMessageHasFieldNumberSet(msg, Message2_FieldNumber_OptionalBytes));
+ [msg release];
+ }
+
+ { // optionalGroup
+ Message2 *msg = [[Message2 alloc] init];
+ XCTAssertFalse(GPBMessageHasFieldNumberSet(msg, Message2_FieldNumber_OptionalGroup));
+ msg.optionalGroup = [Message2_OptionalGroup message];
+ XCTAssertTrue(GPBMessageHasFieldNumberSet(msg, Message2_FieldNumber_OptionalGroup));
+ msg.optionalGroup = nil;
+ XCTAssertFalse(GPBMessageHasFieldNumberSet(msg, Message2_FieldNumber_OptionalGroup));
+ [msg release];
+ }
+
+ { // optionalMessage
+ Message2 *msg = [[Message2 alloc] init];
+ XCTAssertFalse(GPBMessageHasFieldNumberSet(msg, Message2_FieldNumber_OptionalMessage));
+ msg.optionalMessage = [Message2 message];
+ XCTAssertTrue(GPBMessageHasFieldNumberSet(msg, Message2_FieldNumber_OptionalMessage));
+ msg.optionalMessage = nil;
+ XCTAssertFalse(GPBMessageHasFieldNumberSet(msg, Message2_FieldNumber_OptionalMessage));
+ [msg release];
+ }
+
//%PDDM-EXPAND-END PROTO2_TEST_HAS_FIELDS()
}
- (void)testProto3SingleFieldHasBehavior {
//
- // Setting to any value including the default value (0) should result has*
- // being true.
+ // Setting to any value but the default value (0) should result has*
+ // being true. When set to the default, shouldn't be true.
//
//%PDDM-DEFINE PROTO3_TEST_HAS_FIELD(FIELD, NON_ZERO_VALUE, ZERO_VALUE)
@@ -678,6 +755,17 @@
//% [msg release];
//% }
//%
+//%PDDM-DEFINE PROTO3_TEST_CLEAR_FIELD_WITH_NIL(FIELD, VALUE)
+//% { // optional##FIELD
+//% Message3 *msg = [[Message3 alloc] init];
+//% XCTAssertFalse(GPBMessageHasFieldNumberSet(msg, Message3_FieldNumber_Optional##FIELD));
+//% msg.optional##FIELD = VALUE;
+//% XCTAssertTrue(GPBMessageHasFieldNumberSet(msg, Message3_FieldNumber_Optional##FIELD));
+//% msg.optional##FIELD = nil;
+//% XCTAssertFalse(GPBMessageHasFieldNumberSet(msg, Message3_FieldNumber_Optional##FIELD));
+//% [msg release];
+//% }
+//%
//%PDDM-DEFINE PROTO3_TEST_HAS_FIELDS()
//%PROTO3_TEST_HAS_FIELD(Int32, 1, 0)
//%PROTO3_TEST_HAS_FIELD(Int64, 1, 0)
@@ -695,10 +783,17 @@
//%PROTO3_TEST_HAS_FIELD(String, @"foo", @"")
//%PROTO3_TEST_HAS_FIELD(Bytes, [@"foo" dataUsingEncoding:NSUTF8StringEncoding], [NSData data])
//% //
-//% // Test doesn't apply to optionalGroup/optionalMessage.
+//% // Test doesn't apply to optionalMessage (no groups in proto3).
//% //
//%
//%PROTO3_TEST_HAS_FIELD(Enum, Message3_Enum_Bar, Message3_Enum_Foo)
+//% //
+//% // Nil can also be used to clear strings, bytes, and messages (no groups in proto3).
+//% //
+//%
+//%PROTO3_TEST_CLEAR_FIELD_WITH_NIL(String, @"foo")
+//%PROTO3_TEST_CLEAR_FIELD_WITH_NIL(Bytes, [@"foo" dataUsingEncoding:NSUTF8StringEncoding])
+//%PROTO3_TEST_CLEAR_FIELD_WITH_NIL(Message, [Message3 message])
//%PDDM-EXPAND PROTO3_TEST_HAS_FIELDS()
// This block of code is generated, do not edit it directly.
@@ -853,7 +948,7 @@
}
//
- // Test doesn't apply to optionalGroup/optionalMessage.
+ // Test doesn't apply to optionalMessage (no groups in proto3).
//
{ // optionalEnum
@@ -866,6 +961,40 @@
[msg release];
}
+ //
+ // Nil can also be used to clear strings, bytes, and messages (no groups in proto3).
+ //
+
+ { // optionalString
+ Message3 *msg = [[Message3 alloc] init];
+ XCTAssertFalse(GPBMessageHasFieldNumberSet(msg, Message3_FieldNumber_OptionalString));
+ msg.optionalString = @"foo";
+ XCTAssertTrue(GPBMessageHasFieldNumberSet(msg, Message3_FieldNumber_OptionalString));
+ msg.optionalString = nil;
+ XCTAssertFalse(GPBMessageHasFieldNumberSet(msg, Message3_FieldNumber_OptionalString));
+ [msg release];
+ }
+
+ { // optionalBytes
+ Message3 *msg = [[Message3 alloc] init];
+ XCTAssertFalse(GPBMessageHasFieldNumberSet(msg, Message3_FieldNumber_OptionalBytes));
+ msg.optionalBytes = [@"foo" dataUsingEncoding:NSUTF8StringEncoding];
+ XCTAssertTrue(GPBMessageHasFieldNumberSet(msg, Message3_FieldNumber_OptionalBytes));
+ msg.optionalBytes = nil;
+ XCTAssertFalse(GPBMessageHasFieldNumberSet(msg, Message3_FieldNumber_OptionalBytes));
+ [msg release];
+ }
+
+ { // optionalMessage
+ Message3 *msg = [[Message3 alloc] init];
+ XCTAssertFalse(GPBMessageHasFieldNumberSet(msg, Message3_FieldNumber_OptionalMessage));
+ msg.optionalMessage = [Message3 message];
+ XCTAssertTrue(GPBMessageHasFieldNumberSet(msg, Message3_FieldNumber_OptionalMessage));
+ msg.optionalMessage = nil;
+ XCTAssertFalse(GPBMessageHasFieldNumberSet(msg, Message3_FieldNumber_OptionalMessage));
+ [msg release];
+ }
+
//%PDDM-EXPAND-END PROTO3_TEST_HAS_FIELDS()
}
@@ -1972,6 +2101,262 @@
[msg release];
}
+- (void)testProto2OneofSetToDefault {
+
+ // proto3 doesn't normally write out zero (default) fields, but if they are
+ // in a oneof it does. proto2 doesn't have this special behavior, but we
+ // still confirm setting to the explicit default does set the case to be
+ // sure the runtime is working correctly.
+
+ NSString *oneofStringDefault = @"string";
+ NSData *oneofBytesDefault = [@"data" dataUsingEncoding:NSUTF8StringEncoding];
+
+ Message2 *msg = [[Message2 alloc] init];
+
+ uint32_t values[] = {
+ Message2_O_OneOfCase_OneofInt32,
+ Message2_O_OneOfCase_OneofInt64,
+ Message2_O_OneOfCase_OneofUint32,
+ Message2_O_OneOfCase_OneofUint64,
+ Message2_O_OneOfCase_OneofSint32,
+ Message2_O_OneOfCase_OneofSint64,
+ Message2_O_OneOfCase_OneofFixed32,
+ Message2_O_OneOfCase_OneofFixed64,
+ Message2_O_OneOfCase_OneofSfixed32,
+ Message2_O_OneOfCase_OneofSfixed64,
+ Message2_O_OneOfCase_OneofFloat,
+ Message2_O_OneOfCase_OneofDouble,
+ Message2_O_OneOfCase_OneofBool,
+ Message2_O_OneOfCase_OneofString,
+ Message2_O_OneOfCase_OneofBytes,
+ // Skip group
+ // Skip message
+ Message2_O_OneOfCase_OneofEnum,
+ };
+
+ for (size_t i = 0; i < GPBARRAYSIZE(values); ++i) {
+ switch (values[i]) {
+ case Message2_O_OneOfCase_OneofInt32:
+ msg.oneofInt32 = 100;
+ break;
+ case Message2_O_OneOfCase_OneofInt64:
+ msg.oneofInt64 = 101;
+ break;
+ case Message2_O_OneOfCase_OneofUint32:
+ msg.oneofUint32 = 102;
+ break;
+ case Message2_O_OneOfCase_OneofUint64:
+ msg.oneofUint64 = 103;
+ break;
+ case Message2_O_OneOfCase_OneofSint32:
+ msg.oneofSint32 = 104;
+ break;
+ case Message2_O_OneOfCase_OneofSint64:
+ msg.oneofSint64 = 105;
+ break;
+ case Message2_O_OneOfCase_OneofFixed32:
+ msg.oneofFixed32 = 106;
+ break;
+ case Message2_O_OneOfCase_OneofFixed64:
+ msg.oneofFixed64 = 107;
+ break;
+ case Message2_O_OneOfCase_OneofSfixed32:
+ msg.oneofSfixed32 = 108;
+ break;
+ case Message2_O_OneOfCase_OneofSfixed64:
+ msg.oneofSfixed64 = 109;
+ break;
+ case Message2_O_OneOfCase_OneofFloat:
+ msg.oneofFloat = 110.0f;
+ break;
+ case Message2_O_OneOfCase_OneofDouble:
+ msg.oneofDouble = 111.0;
+ break;
+ case Message2_O_OneOfCase_OneofBool:
+ msg.oneofBool = YES;
+ break;
+ case Message2_O_OneOfCase_OneofString:
+ msg.oneofString = oneofStringDefault;
+ break;
+ case Message2_O_OneOfCase_OneofBytes:
+ msg.oneofBytes = oneofBytesDefault;
+ break;
+ case Message2_O_OneOfCase_OneofEnum:
+ msg.oneofEnum = Message3_Enum_Baz;
+ break;
+ default:
+ XCTFail(@"shouldn't happen, loop: %zd, value: %d", i, values[i]);
+ break;
+ }
+
+ // Should be set to the correct case.
+ XCTAssertEqual(msg.oOneOfCase, values[i], "Loop: %zd", i);
+
+ // Confirm everything is the defaults.
+ XCTAssertEqual(msg.oneofInt32, 100, "Loop: %zd", i);
+ XCTAssertEqual(msg.oneofInt64, 101, "Loop: %zd", i);
+ XCTAssertEqual(msg.oneofUint32, 102U, "Loop: %zd", i);
+ XCTAssertEqual(msg.oneofUint64, 103U, "Loop: %zd", i);
+ XCTAssertEqual(msg.oneofSint32, 104, "Loop: %zd", i);
+ XCTAssertEqual(msg.oneofSint64, 105, "Loop: %zd", i);
+ XCTAssertEqual(msg.oneofFixed32, 106U, "Loop: %zd", i);
+ XCTAssertEqual(msg.oneofFixed64, 107U, "Loop: %zd", i);
+ XCTAssertEqual(msg.oneofSfixed32, 108, "Loop: %zd", i);
+ XCTAssertEqual(msg.oneofSfixed64, 109, "Loop: %zd", i);
+ XCTAssertEqual(msg.oneofFloat, 110.0f, "Loop: %zd", i);
+ XCTAssertEqual(msg.oneofDouble, 111.0, "Loop: %zd", i);
+ XCTAssertEqual(msg.oneofBool, YES, "Loop: %zd", i);
+ XCTAssertEqualObjects(msg.oneofString, oneofStringDefault, "Loop: %zd", i);
+ XCTAssertEqualObjects(msg.oneofBytes, oneofBytesDefault, "Loop: %zd", i);
+ // Skip group, no default to consider.
+ // Skip message, no default to consider.
+ XCTAssertEqual(msg.oneofEnum, Message2_Enum_Baz, "Loop: %zd", i);
+ }
+
+ // We special case nil on string, data, group, and message, ensure they work
+ // as expected. i.e. - it clears the case.
+ msg.oneofString = nil;
+ XCTAssertEqualObjects(msg.oneofString, oneofStringDefault);
+ XCTAssertEqual(msg.oOneOfCase, Message2_O_OneOfCase_GPBUnsetOneOfCase);
+ msg.oneofBytes = nil;
+ XCTAssertEqual(msg.oOneOfCase, Message2_O_OneOfCase_GPBUnsetOneOfCase);
+ XCTAssertEqualObjects(msg.oneofBytes, oneofBytesDefault);
+ msg.oneofGroup = nil;
+ XCTAssertEqual(msg.oOneOfCase, Message2_O_OneOfCase_GPBUnsetOneOfCase);
+ XCTAssertNotNil(msg.oneofGroup);
+ msg.oneofMessage = nil;
+ XCTAssertEqual(msg.oOneOfCase, Message2_O_OneOfCase_GPBUnsetOneOfCase);
+ XCTAssertNotNil(msg.oneofMessage);
+
+ [msg release];
+}
+
+- (void)testProto3OneofSetToZero {
+
+ // Normally setting a proto3 field to the zero value should result in it being
+ // reset/cleared. But in a oneof, it still gets recored so it can go out
+ // over the wire and the other side can see what was set in the oneof.
+
+ NSString *oneofStringDefault = @"";
+ NSData *oneofBytesDefault = [NSData data];
+
+ Message3 *msg = [[Message3 alloc] init];
+
+ uint32_t values[] = {
+ Message3_O_OneOfCase_OneofInt32,
+ Message3_O_OneOfCase_OneofInt64,
+ Message3_O_OneOfCase_OneofUint32,
+ Message3_O_OneOfCase_OneofUint64,
+ Message3_O_OneOfCase_OneofSint32,
+ Message3_O_OneOfCase_OneofSint64,
+ Message3_O_OneOfCase_OneofFixed32,
+ Message3_O_OneOfCase_OneofFixed64,
+ Message3_O_OneOfCase_OneofSfixed32,
+ Message3_O_OneOfCase_OneofSfixed64,
+ Message3_O_OneOfCase_OneofFloat,
+ Message3_O_OneOfCase_OneofDouble,
+ Message3_O_OneOfCase_OneofBool,
+ Message3_O_OneOfCase_OneofString,
+ Message3_O_OneOfCase_OneofBytes,
+ Message3_O_OneOfCase_OneofMessage,
+ Message3_O_OneOfCase_OneofEnum,
+ };
+
+ for (size_t i = 0; i < GPBARRAYSIZE(values); ++i) {
+ switch (values[i]) {
+ case Message3_O_OneOfCase_OneofInt32:
+ msg.oneofInt32 = 0;
+ break;
+ case Message3_O_OneOfCase_OneofInt64:
+ msg.oneofInt64 = 0;
+ break;
+ case Message3_O_OneOfCase_OneofUint32:
+ msg.oneofUint32 = 0;
+ break;
+ case Message3_O_OneOfCase_OneofUint64:
+ msg.oneofUint64 = 0;
+ break;
+ case Message3_O_OneOfCase_OneofSint32:
+ msg.oneofSint32 = 0;
+ break;
+ case Message3_O_OneOfCase_OneofSint64:
+ msg.oneofSint64 = 0;
+ break;
+ case Message3_O_OneOfCase_OneofFixed32:
+ msg.oneofFixed32 = 0;
+ break;
+ case Message3_O_OneOfCase_OneofFixed64:
+ msg.oneofFixed64 = 0;
+ break;
+ case Message3_O_OneOfCase_OneofSfixed32:
+ msg.oneofSfixed32 = 0;
+ break;
+ case Message3_O_OneOfCase_OneofSfixed64:
+ msg.oneofSfixed64 = 0;
+ break;
+ case Message3_O_OneOfCase_OneofFloat:
+ msg.oneofFloat = 0.0f;
+ break;
+ case Message3_O_OneOfCase_OneofDouble:
+ msg.oneofDouble = 0.0;
+ break;
+ case Message3_O_OneOfCase_OneofBool:
+ msg.oneofBool = NO;
+ break;
+ case Message3_O_OneOfCase_OneofString:
+ msg.oneofString = oneofStringDefault;
+ break;
+ case Message3_O_OneOfCase_OneofBytes:
+ msg.oneofBytes = oneofBytesDefault;
+ break;
+ case Message3_O_OneOfCase_OneofMessage:
+ msg.oneofMessage.optionalInt32 = 0;
+ break;
+ case Message3_O_OneOfCase_OneofEnum:
+ msg.oneofEnum = Message3_Enum_Foo;
+ break;
+ default:
+ XCTFail(@"shouldn't happen, loop: %zd, value: %d", i, values[i]);
+ break;
+ }
+
+ // Should be set to the correct case.
+ XCTAssertEqual(msg.oOneOfCase, values[i], "Loop: %zd", i);
+
+ // Confirm everything is still zeros.
+ XCTAssertEqual(msg.oneofInt32, 0, "Loop: %zd", i);
+ XCTAssertEqual(msg.oneofInt64, 0, "Loop: %zd", i);
+ XCTAssertEqual(msg.oneofUint32, 0U, "Loop: %zd", i);
+ XCTAssertEqual(msg.oneofUint64, 0U, "Loop: %zd", i);
+ XCTAssertEqual(msg.oneofSint32, 0, "Loop: %zd", i);
+ XCTAssertEqual(msg.oneofSint64, 0, "Loop: %zd", i);
+ XCTAssertEqual(msg.oneofFixed32, 0U, "Loop: %zd", i);
+ XCTAssertEqual(msg.oneofFixed64, 0U, "Loop: %zd", i);
+ XCTAssertEqual(msg.oneofSfixed32, 0, "Loop: %zd", i);
+ XCTAssertEqual(msg.oneofSfixed64, 0, "Loop: %zd", i);
+ XCTAssertEqual(msg.oneofFloat, 0.0f, "Loop: %zd", i);
+ XCTAssertEqual(msg.oneofDouble, 0.0, "Loop: %zd", i);
+ XCTAssertEqual(msg.oneofBool, NO, "Loop: %zd", i);
+ XCTAssertEqualObjects(msg.oneofString, oneofStringDefault, "Loop: %zd", i);
+ XCTAssertEqualObjects(msg.oneofBytes, oneofBytesDefault, "Loop: %zd", i);
+ XCTAssertNotNil(msg.oneofMessage, "Loop: %zd", i);
+ XCTAssertEqual(msg.oneofEnum, Message3_Enum_Foo, "Loop: %zd", i);
+ }
+
+ // We special case nil on string, data, message, ensure they work as expected.
+ msg.oneofString = nil;
+ XCTAssertEqual(msg.oOneOfCase, Message3_O_OneOfCase_GPBUnsetOneOfCase);
+ XCTAssertEqualObjects(msg.oneofString, oneofStringDefault);
+ msg.oneofBytes = nil;
+ XCTAssertEqual(msg.oOneOfCase, Message3_O_OneOfCase_GPBUnsetOneOfCase);
+ XCTAssertEqualObjects(msg.oneofBytes, oneofBytesDefault);
+ msg.oneofMessage = nil;
+ XCTAssertEqual(msg.oOneOfCase, Message3_O_OneOfCase_GPBUnsetOneOfCase);
+ XCTAssertNotNil(msg.oneofMessage);
+
+ [msg release];
+}
+
- (void)testCopyingMakesUniqueObjects {
const int repeatCount = 5;
TestAllTypes *msg1 = [TestAllTypes message];
diff --git a/objectivec/Tests/GPBUnittestProtos.m b/objectivec/Tests/GPBUnittestProtos.m
index d19beee9..8d2948bf 100644
--- a/objectivec/Tests/GPBUnittestProtos.m
+++ b/objectivec/Tests/GPBUnittestProtos.m
@@ -36,6 +36,7 @@
// a descriptor as it doesn't use the classes/enums.
#import "google/protobuf/Descriptor.pbobjc.m"
+#import "google/protobuf/AnyTest.pbobjc.m"
#import "google/protobuf/MapProto2Unittest.pbobjc.m"
#import "google/protobuf/MapUnittest.pbobjc.m"
#import "google/protobuf/Unittest.pbobjc.m"
@@ -62,3 +63,11 @@
#import "google/protobuf/UnittestPreserveUnknownEnum.pbobjc.m"
#import "google/protobuf/UnittestRuntimeProto2.pbobjc.m"
#import "google/protobuf/UnittestRuntimeProto3.pbobjc.m"
+
+#import "google/protobuf/UnittestExtensionChainA.pbobjc.m"
+#import "google/protobuf/UnittestExtensionChainB.pbobjc.m"
+#import "google/protobuf/UnittestExtensionChainC.pbobjc.m"
+#import "google/protobuf/UnittestExtensionChainD.pbobjc.m"
+#import "google/protobuf/UnittestExtensionChainE.pbobjc.m"
+// See GPBUnittestProtos2.m for for "UnittestExtensionChainF.pbobjc.m"
+#import "google/protobuf/UnittestExtensionChainG.pbobjc.m"
diff --git a/objectivec/Tests/GPBUnittestProtos2.m b/objectivec/Tests/GPBUnittestProtos2.m
new file mode 100644
index 00000000..ef9f0702
--- /dev/null
+++ b/objectivec/Tests/GPBUnittestProtos2.m
@@ -0,0 +1,34 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2016 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.
+
+// This one file in the chain tests is compiled by itself to ensure if was
+// generated with the extra #imports needed to pull in the indirect Root class
+// used in its Root registry.
+#import "google/protobuf/UnittestExtensionChainF.pbobjc.m"
diff --git a/objectivec/Tests/GPBWellKnownTypesTest.m b/objectivec/Tests/GPBWellKnownTypesTest.m
index 78f4e637..041841dd 100644
--- a/objectivec/Tests/GPBWellKnownTypesTest.m
+++ b/objectivec/Tests/GPBWellKnownTypesTest.m
@@ -32,6 +32,8 @@
#import <XCTest/XCTest.h>
+#import "google/protobuf/AnyTest.pbobjc.h"
+
// A basically random interval into the future for testing with.
static const NSTimeInterval kFutureOffsetInterval = 15000;
@@ -99,4 +101,58 @@ static const NSTimeInterval kTimeAccuracy = 1e-9;
[duration2 release];
}
+- (void)testAnyHelpers {
+
+ // Set and extract covers most of the code.
+
+ TestAny *subMessage = [TestAny message];
+ subMessage.int32Value = 12345;
+ TestAny *message = [TestAny message];
+ NSError *err = nil;
+ message.anyValue = [GPBAny anyWithMessage:subMessage error:&err];
+ XCTAssertNil(err);
+
+ NSData *data = message.data;
+ XCTAssertNotNil(data);
+
+ TestAny *message2 = [TestAny parseFromData:data error:&err];
+ XCTAssertNil(err);
+ XCTAssertNotNil(message2);
+ XCTAssertTrue(message2.hasAnyValue);
+
+ TestAny *subMessage2 =
+ (TestAny *)[message.anyValue unpackMessageClass:[TestAny class]
+ error:&err];
+ XCTAssertNil(err);
+ XCTAssertNotNil(subMessage2);
+ XCTAssertEqual(subMessage2.int32Value, 12345);
+
+ // NULL errorPtr in the two calls.
+
+ message.anyValue = [GPBAny anyWithMessage:subMessage error:NULL];
+ NSData *data2 = message.data;
+ XCTAssertEqualObjects(data2, data);
+
+ TestAny *subMessage3 =
+ (TestAny *)[message.anyValue unpackMessageClass:[TestAny class]
+ error:NULL];
+ XCTAssertNotNil(subMessage3);
+ XCTAssertEqualObjects(subMessage2, subMessage3);
+
+ // Try to extract wrong type.
+
+ GPBTimestamp *wrongMessage =
+ (GPBTimestamp *)[message.anyValue unpackMessageClass:[GPBTimestamp class]
+ error:&err];
+ XCTAssertNotNil(err);
+ XCTAssertNil(wrongMessage);
+ XCTAssertEqualObjects(err.domain, GPBWellKnownTypesErrorDomain);
+ XCTAssertEqual(err.code, GPBWellKnownTypesErrorCodeTypeURLMismatch);
+
+ wrongMessage =
+ (GPBTimestamp *)[message.anyValue unpackMessageClass:[GPBTimestamp class]
+ error:NULL];
+ XCTAssertNil(wrongMessage);
+}
+
@end
diff --git a/objectivec/Tests/unittest_cycle.proto b/objectivec/Tests/unittest_cycle.proto
index 5f6f56a1..afc1b0fe 100644
--- a/objectivec/Tests/unittest_cycle.proto
+++ b/objectivec/Tests/unittest_cycle.proto
@@ -31,10 +31,8 @@ syntax = "proto2";
package protobuf_unittest;
-// Cycles in the Message graph can cause problems for the mutable classes
-// since the properties on the mutable class change types. This file just
-// needs to generate source, and that source must compile, to ensure the
-// generated source works for this sort of case.
+// Cycles in the Message graph can cause problems for message class
+// initialization order.
// You can't make a object graph that spans files, so this can only be done
// within a single proto file.
diff --git a/objectivec/Tests/unittest_extension_chain_a.proto b/objectivec/Tests/unittest_extension_chain_a.proto
new file mode 100644
index 00000000..6a227eb9
--- /dev/null
+++ b/objectivec/Tests/unittest_extension_chain_a.proto
@@ -0,0 +1,51 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2016 Google Inc. All rights reserved.
+//
+// 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.
+
+syntax = "proto2";
+
+package protobuf_unittest;
+
+import "google/protobuf/unittest.proto";
+
+import "unittest_extension_chain_b.proto";
+import "unittest_extension_chain_c.proto";
+import "unittest_extension_chain_d.proto";
+
+// The Root for this file should end up adding the local extension and merging
+// in the extensions from D's Root (unittest and C will come via D's).
+
+message ChainAMessage {
+ optional ChainBMessage b = 1;
+ optional ChainCMessage c = 2;
+ optional ChainDMessage d = 3;
+}
+
+extend TestAllExtensions {
+ optional int32 chain_a_extension = 10001;
+}
diff --git a/objectivec/Tests/unittest_extension_chain_b.proto b/objectivec/Tests/unittest_extension_chain_b.proto
new file mode 100644
index 00000000..0da7ed3e
--- /dev/null
+++ b/objectivec/Tests/unittest_extension_chain_b.proto
@@ -0,0 +1,47 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2016 Google Inc. All rights reserved.
+//
+// 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.
+
+syntax = "proto2";
+
+package protobuf_unittest;
+
+import "google/protobuf/unittest.proto";
+
+import "unittest_extension_chain_c.proto";
+
+// The Root for this file should end up adding the local extension and merging
+// in the extensions from C's Root (unittest will come via C's).
+
+message ChainBMessage {
+ optional ChainCMessage c = 1;
+}
+
+extend TestAllExtensions {
+ optional int32 chain_b_extension = 10002;
+}
diff --git a/objectivec/Tests/unittest_extension_chain_c.proto b/objectivec/Tests/unittest_extension_chain_c.proto
new file mode 100644
index 00000000..c702900a
--- /dev/null
+++ b/objectivec/Tests/unittest_extension_chain_c.proto
@@ -0,0 +1,45 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2016 Google Inc. All rights reserved.
+//
+// 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.
+
+syntax = "proto2";
+
+package protobuf_unittest;
+
+import "google/protobuf/unittest.proto";
+
+// The Root for this file should end up adding the local extension and merging
+// in the extensions from unittest.proto's Root.
+
+message ChainCMessage {
+ optional int32 my_field = 1;
+}
+
+extend TestAllExtensions {
+ optional int32 chain_c_extension = 10003;
+}
diff --git a/objectivec/Tests/unittest_extension_chain_d.proto b/objectivec/Tests/unittest_extension_chain_d.proto
new file mode 100644
index 00000000..f9abe3ba
--- /dev/null
+++ b/objectivec/Tests/unittest_extension_chain_d.proto
@@ -0,0 +1,49 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2016 Google Inc. All rights reserved.
+//
+// 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.
+
+syntax = "proto2";
+
+package protobuf_unittest;
+
+import "google/protobuf/unittest.proto";
+
+import "unittest_extension_chain_b.proto";
+import "unittest_extension_chain_c.proto";
+
+// The root should end up needing to merge B (C will be merged into B, so it
+// doesn't need to be directly merged).
+
+message ChainDMessage {
+ optional ChainBMessage b = 1;
+ optional ChainCMessage c = 2;
+}
+
+extend TestAllExtensions {
+ optional int32 chain_d_extension = 10004;
+}
diff --git a/objectivec/Tests/unittest_extension_chain_e.proto b/objectivec/Tests/unittest_extension_chain_e.proto
new file mode 100644
index 00000000..fe116631
--- /dev/null
+++ b/objectivec/Tests/unittest_extension_chain_e.proto
@@ -0,0 +1,40 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2016 Google Inc. All rights reserved.
+//
+// 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.
+
+syntax = "proto2";
+
+package protobuf_unittest;
+
+import "google/protobuf/unittest.proto";
+
+// The Root for this file should end up just merging in unittest's Root.
+
+message ChainEMessage {
+ optional TestAllTypes my_field = 1;
+}
diff --git a/objectivec/Tests/unittest_extension_chain_f.proto b/objectivec/Tests/unittest_extension_chain_f.proto
new file mode 100644
index 00000000..b9bed723
--- /dev/null
+++ b/objectivec/Tests/unittest_extension_chain_f.proto
@@ -0,0 +1,44 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2016 Google Inc. All rights reserved.
+//
+// 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.
+
+syntax = "proto2";
+
+package protobuf_unittest;
+
+import "unittest_extension_chain_g.proto";
+
+// The Root for this file should just be merging in the extensions from C's
+// Root (because G doens't define anything itself).
+
+// The generated source will also have to directly import C's .h file so it can
+// compile the reference to C's Root class.
+
+message ChainFMessage {
+ optional ChainGMessage g = 1;
+}
diff --git a/objectivec/Tests/unittest_extension_chain_g.proto b/objectivec/Tests/unittest_extension_chain_g.proto
new file mode 100644
index 00000000..aee827b1
--- /dev/null
+++ b/objectivec/Tests/unittest_extension_chain_g.proto
@@ -0,0 +1,41 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2016 Google Inc. All rights reserved.
+//
+// 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.
+
+syntax = "proto2";
+
+package protobuf_unittest;
+
+import "unittest_extension_chain_c.proto";
+
+// The Root for this file should just be merging in the extensions from C's
+// Root.
+
+message ChainGMessage {
+ optional ChainCMessage c = 1;
+}
diff --git a/objectivec/Tests/unittest_objc.proto b/objectivec/Tests/unittest_objc.proto
index 914945eb..e5577faf 100644
--- a/objectivec/Tests/unittest_objc.proto
+++ b/objectivec/Tests/unittest_objc.proto
@@ -34,6 +34,15 @@ import "google/protobuf/unittest.proto";
package protobuf_unittest;
+// Used to check that Headerdocs and appledoc work correctly. If these comments
+// are not handled correctly, Xcode will fail to build the tests.
+message TestGeneratedComments {
+ // This is a string that could contain stuff like
+ // mime types as image/* or */plain. Maybe twitter usernames
+ // like @protobuf, @google or @something.
+ optional string string_field = 1;
+}
+
// Using the messages in unittest.proto, setup for recursive cases for testing
// extensions at various depths.
extend TestAllExtensions {