[ObjC] Add tests for proto3 optional behaviors.
diff --git a/objectivec/DevTools/compile_testing_protos.sh b/objectivec/DevTools/compile_testing_protos.sh
index dc1d6d2..d04c5c5 100755
--- a/objectivec/DevTools/compile_testing_protos.sh
+++ b/objectivec/DevTools/compile_testing_protos.sh
@@ -158,6 +158,7 @@
--objc_out="${OUTPUT_DIR}/google/protobuf" \
--proto_path=src/google/protobuf/ \
--proto_path=src \
+ --experimental_allow_proto3_optional \
"$@"
}
diff --git a/objectivec/Tests/GPBMessageTests+Runtime.m b/objectivec/Tests/GPBMessageTests+Runtime.m
index aa5b0db..1dac797 100644
--- a/objectivec/Tests/GPBMessageTests+Runtime.m
+++ b/objectivec/Tests/GPBMessageTests+Runtime.m
@@ -270,6 +270,23 @@
@"field: %@", name);
}
+ // Single Optional fields
+ // - has*/setHas* thanks to the optional keyword in proto3, they exist
+ // for primitive types.
+ // - has*/setHas* valid for Message.
+
+ for (NSString *name in names) {
+ // build the selector, i.e. - hasOptionalInt32/setHasOptionalInt32:
+ SEL hasSel = NSSelectorFromString(
+ [NSString stringWithFormat:@"hasOptional%@", name]);
+ SEL setHasSel = NSSelectorFromString(
+ [NSString stringWithFormat:@"setHasOptional%@:", name]);
+ XCTAssertTrue([Message3Optional instancesRespondToSelector:hasSel], @"field: %@",
+ name);
+ XCTAssertTrue([Message3Optional instancesRespondToSelector:setHasSel],
+ @"field: %@", name);
+ }
+
// map<> fields
// - no has*/setHas*
// - *Count
@@ -1002,6 +1019,249 @@
//%PDDM-EXPAND-END PROTO3_TEST_HAS_FIELDS()
}
+- (void)testProto3SingleOptionalFieldHasBehavior {
+ //
+ // Setting to any value including the default (0) should result in true.
+ //
+
+//%PDDM-DEFINE PROTO3_TEST_OPTIONAL_HAS_FIELD(FIELD, NON_ZERO_VALUE, ZERO_VALUE)
+//% { // optional##FIELD
+//% Message3Optional *msg = [[Message3Optional alloc] init];
+//% XCTAssertFalse(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_Optional##FIELD));
+//% msg.optional##FIELD = NON_ZERO_VALUE;
+//% XCTAssertTrue(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_Optional##FIELD));
+//% msg.hasOptional##FIELD = NO;
+//% XCTAssertFalse(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_Optional##FIELD));
+//% msg.optional##FIELD = ZERO_VALUE;
+//% XCTAssertTrue(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_Optional##FIELD));
+//% [msg release];
+//% }
+//%
+//%PDDM-DEFINE PROTO3_TEST_OPTIONAL_HAS_FIELDS()
+//%PROTO3_TEST_OPTIONAL_HAS_FIELD(Int32, 1, 0)
+//%PROTO3_TEST_OPTIONAL_HAS_FIELD(Int64, 1, 0)
+//%PROTO3_TEST_OPTIONAL_HAS_FIELD(Uint32, 1, 0)
+//%PROTO3_TEST_OPTIONAL_HAS_FIELD(Uint64, 1, 0)
+//%PROTO3_TEST_OPTIONAL_HAS_FIELD(Sint32, 1, 0)
+//%PROTO3_TEST_OPTIONAL_HAS_FIELD(Sint64, 1, 0)
+//%PROTO3_TEST_OPTIONAL_HAS_FIELD(Fixed32, 1, 0)
+//%PROTO3_TEST_OPTIONAL_HAS_FIELD(Fixed64, 1, 0)
+//%PROTO3_TEST_OPTIONAL_HAS_FIELD(Sfixed32, 1, 0)
+//%PROTO3_TEST_OPTIONAL_HAS_FIELD(Sfixed64, 1, 0)
+//%PROTO3_TEST_OPTIONAL_HAS_FIELD(Float, 1.0f, 0.0f)
+//%PROTO3_TEST_OPTIONAL_HAS_FIELD(Double, 1.0, 0.0)
+//%PROTO3_TEST_OPTIONAL_HAS_FIELD(Bool, YES, NO)
+//%PROTO3_TEST_OPTIONAL_HAS_FIELD(String, @"foo", @"")
+//%PROTO3_TEST_OPTIONAL_HAS_FIELD(Bytes, [@"foo" dataUsingEncoding:NSUTF8StringEncoding], [NSData data])
+//% //
+//% // Test doesn't apply to optionalMessage (no groups in proto3).
+//% //
+//%
+//%PROTO3_TEST_OPTIONAL_HAS_FIELD(Enum, Message3Optional_Enum_Bar, Message3Optional_Enum_Foo)
+//%PDDM-EXPAND PROTO3_TEST_OPTIONAL_HAS_FIELDS()
+// This block of code is generated, do not edit it directly.
+// clang-format off
+
+ { // optionalInt32
+ Message3Optional *msg = [[Message3Optional alloc] init];
+ XCTAssertFalse(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalInt32));
+ msg.optionalInt32 = 1;
+ XCTAssertTrue(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalInt32));
+ msg.hasOptionalInt32 = NO;
+ XCTAssertFalse(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalInt32));
+ msg.optionalInt32 = 0;
+ XCTAssertTrue(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalInt32));
+ [msg release];
+ }
+
+ { // optionalInt64
+ Message3Optional *msg = [[Message3Optional alloc] init];
+ XCTAssertFalse(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalInt64));
+ msg.optionalInt64 = 1;
+ XCTAssertTrue(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalInt64));
+ msg.hasOptionalInt64 = NO;
+ XCTAssertFalse(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalInt64));
+ msg.optionalInt64 = 0;
+ XCTAssertTrue(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalInt64));
+ [msg release];
+ }
+
+ { // optionalUint32
+ Message3Optional *msg = [[Message3Optional alloc] init];
+ XCTAssertFalse(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalUint32));
+ msg.optionalUint32 = 1;
+ XCTAssertTrue(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalUint32));
+ msg.hasOptionalUint32 = NO;
+ XCTAssertFalse(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalUint32));
+ msg.optionalUint32 = 0;
+ XCTAssertTrue(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalUint32));
+ [msg release];
+ }
+
+ { // optionalUint64
+ Message3Optional *msg = [[Message3Optional alloc] init];
+ XCTAssertFalse(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalUint64));
+ msg.optionalUint64 = 1;
+ XCTAssertTrue(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalUint64));
+ msg.hasOptionalUint64 = NO;
+ XCTAssertFalse(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalUint64));
+ msg.optionalUint64 = 0;
+ XCTAssertTrue(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalUint64));
+ [msg release];
+ }
+
+ { // optionalSint32
+ Message3Optional *msg = [[Message3Optional alloc] init];
+ XCTAssertFalse(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalSint32));
+ msg.optionalSint32 = 1;
+ XCTAssertTrue(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalSint32));
+ msg.hasOptionalSint32 = NO;
+ XCTAssertFalse(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalSint32));
+ msg.optionalSint32 = 0;
+ XCTAssertTrue(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalSint32));
+ [msg release];
+ }
+
+ { // optionalSint64
+ Message3Optional *msg = [[Message3Optional alloc] init];
+ XCTAssertFalse(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalSint64));
+ msg.optionalSint64 = 1;
+ XCTAssertTrue(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalSint64));
+ msg.hasOptionalSint64 = NO;
+ XCTAssertFalse(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalSint64));
+ msg.optionalSint64 = 0;
+ XCTAssertTrue(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalSint64));
+ [msg release];
+ }
+
+ { // optionalFixed32
+ Message3Optional *msg = [[Message3Optional alloc] init];
+ XCTAssertFalse(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalFixed32));
+ msg.optionalFixed32 = 1;
+ XCTAssertTrue(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalFixed32));
+ msg.hasOptionalFixed32 = NO;
+ XCTAssertFalse(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalFixed32));
+ msg.optionalFixed32 = 0;
+ XCTAssertTrue(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalFixed32));
+ [msg release];
+ }
+
+ { // optionalFixed64
+ Message3Optional *msg = [[Message3Optional alloc] init];
+ XCTAssertFalse(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalFixed64));
+ msg.optionalFixed64 = 1;
+ XCTAssertTrue(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalFixed64));
+ msg.hasOptionalFixed64 = NO;
+ XCTAssertFalse(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalFixed64));
+ msg.optionalFixed64 = 0;
+ XCTAssertTrue(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalFixed64));
+ [msg release];
+ }
+
+ { // optionalSfixed32
+ Message3Optional *msg = [[Message3Optional alloc] init];
+ XCTAssertFalse(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalSfixed32));
+ msg.optionalSfixed32 = 1;
+ XCTAssertTrue(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalSfixed32));
+ msg.hasOptionalSfixed32 = NO;
+ XCTAssertFalse(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalSfixed32));
+ msg.optionalSfixed32 = 0;
+ XCTAssertTrue(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalSfixed32));
+ [msg release];
+ }
+
+ { // optionalSfixed64
+ Message3Optional *msg = [[Message3Optional alloc] init];
+ XCTAssertFalse(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalSfixed64));
+ msg.optionalSfixed64 = 1;
+ XCTAssertTrue(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalSfixed64));
+ msg.hasOptionalSfixed64 = NO;
+ XCTAssertFalse(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalSfixed64));
+ msg.optionalSfixed64 = 0;
+ XCTAssertTrue(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalSfixed64));
+ [msg release];
+ }
+
+ { // optionalFloat
+ Message3Optional *msg = [[Message3Optional alloc] init];
+ XCTAssertFalse(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalFloat));
+ msg.optionalFloat = 1.0f;
+ XCTAssertTrue(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalFloat));
+ msg.hasOptionalFloat = NO;
+ XCTAssertFalse(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalFloat));
+ msg.optionalFloat = 0.0f;
+ XCTAssertTrue(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalFloat));
+ [msg release];
+ }
+
+ { // optionalDouble
+ Message3Optional *msg = [[Message3Optional alloc] init];
+ XCTAssertFalse(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalDouble));
+ msg.optionalDouble = 1.0;
+ XCTAssertTrue(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalDouble));
+ msg.hasOptionalDouble = NO;
+ XCTAssertFalse(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalDouble));
+ msg.optionalDouble = 0.0;
+ XCTAssertTrue(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalDouble));
+ [msg release];
+ }
+
+ { // optionalBool
+ Message3Optional *msg = [[Message3Optional alloc] init];
+ XCTAssertFalse(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalBool));
+ msg.optionalBool = YES;
+ XCTAssertTrue(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalBool));
+ msg.hasOptionalBool = NO;
+ XCTAssertFalse(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalBool));
+ msg.optionalBool = NO;
+ XCTAssertTrue(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalBool));
+ [msg release];
+ }
+
+ { // optionalString
+ Message3Optional *msg = [[Message3Optional alloc] init];
+ XCTAssertFalse(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalString));
+ msg.optionalString = @"foo";
+ XCTAssertTrue(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalString));
+ msg.hasOptionalString = NO;
+ XCTAssertFalse(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalString));
+ msg.optionalString = @"";
+ XCTAssertTrue(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalString));
+ [msg release];
+ }
+
+ { // optionalBytes
+ Message3Optional *msg = [[Message3Optional alloc] init];
+ XCTAssertFalse(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalBytes));
+ msg.optionalBytes = [@"foo" dataUsingEncoding:NSUTF8StringEncoding];
+ XCTAssertTrue(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalBytes));
+ msg.hasOptionalBytes = NO;
+ XCTAssertFalse(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalBytes));
+ msg.optionalBytes = [NSData data];
+ XCTAssertTrue(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalBytes));
+ [msg release];
+ }
+
+ //
+ // Test doesn't apply to optionalMessage (no groups in proto3).
+ //
+
+ { // optionalEnum
+ Message3Optional *msg = [[Message3Optional alloc] init];
+ XCTAssertFalse(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalEnum));
+ msg.optionalEnum = Message3Optional_Enum_Bar;
+ XCTAssertTrue(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalEnum));
+ msg.hasOptionalEnum = NO;
+ XCTAssertFalse(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalEnum));
+ msg.optionalEnum = Message3Optional_Enum_Foo;
+ XCTAssertTrue(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalEnum));
+ [msg release];
+ }
+
+// clang-format on
+//%PDDM-EXPAND-END PROTO3_TEST_OPTIONAL_HAS_FIELDS()
+}
+
- (void)testAccessingProto2UnknownEnumValues {
Message2 *msg = [[Message2 alloc] init];
diff --git a/objectivec/Tests/GPBMessageTests+Serialization.m b/objectivec/Tests/GPBMessageTests+Serialization.m
index ef6e589..6f20797 100644
--- a/objectivec/Tests/GPBMessageTests+Serialization.m
+++ b/objectivec/Tests/GPBMessageTests+Serialization.m
@@ -109,6 +109,317 @@
[msg release];
}
+- (void)testProto3SerializationHandlingOptionals {
+ //
+ // Proto3 optionals should be just like proto2, zero values also get serialized.
+ //
+
+//%PDDM-DEFINE PROTO3_TEST_SERIALIZE_OPTIONAL_FIELD(FIELD, ZERO_VALUE, EXPECTED_LEN)
+//% { // optional##FIELD
+//% Message3Optional *msg = [[Message3Optional alloc] init];
+//% NSData *data = [msg data];
+//% XCTAssertEqual([data length], 0U);
+//% msg.optional##FIELD = ZERO_VALUE;
+//% data = [msg data];
+//% XCTAssertEqual(data.length, EXPECTED_LEN);
+//% NSError *err = nil;
+//% Message3Optional *msg2 = [Message3Optional parseFromData:data error:&err];
+//% XCTAssertNotNil(msg2);
+//% XCTAssertNil(err);
+//% XCTAssertTrue(msg2.hasOptional##FIELD);
+//% XCTAssertEqualObjects(msg, msg2);
+//% [msg release];
+//% }
+//%
+//%PDDM-DEFINE PROTO3_TEST_SERIALIZE_OPTIONAL_FIELDS()
+//%PROTO3_TEST_SERIALIZE_OPTIONAL_FIELD(Int32, 0, 2)
+//%PROTO3_TEST_SERIALIZE_OPTIONAL_FIELD(Int64, 0, 2)
+//%PROTO3_TEST_SERIALIZE_OPTIONAL_FIELD(Uint32, 0, 2)
+//%PROTO3_TEST_SERIALIZE_OPTIONAL_FIELD(Uint64, 0, 2)
+//%PROTO3_TEST_SERIALIZE_OPTIONAL_FIELD(Sint32, 0, 2)
+//%PROTO3_TEST_SERIALIZE_OPTIONAL_FIELD(Sint64, 0, 2)
+//%PROTO3_TEST_SERIALIZE_OPTIONAL_FIELD(Fixed32, 0, 5)
+//%PROTO3_TEST_SERIALIZE_OPTIONAL_FIELD(Fixed64, 0, 9)
+//%PROTO3_TEST_SERIALIZE_OPTIONAL_FIELD(Sfixed32, 0, 5)
+//%PROTO3_TEST_SERIALIZE_OPTIONAL_FIELD(Sfixed64, 0, 9)
+//%PROTO3_TEST_SERIALIZE_OPTIONAL_FIELD(Float, 0.0f, 5)
+//%PROTO3_TEST_SERIALIZE_OPTIONAL_FIELD(Double, 0.0, 9)
+//%PROTO3_TEST_SERIALIZE_OPTIONAL_FIELD(Bool, NO, 2)
+//%PROTO3_TEST_SERIALIZE_OPTIONAL_FIELD(String, @"", 2)
+//%PROTO3_TEST_SERIALIZE_OPTIONAL_FIELD(Bytes, [NSData data], 2)
+//% //
+//% // Test doesn't apply to optionalMessage (no groups in proto3).
+//% //
+//%
+//%PROTO3_TEST_SERIALIZE_OPTIONAL_FIELD(Enum, Message3Optional_Enum_Foo, 3)
+//%PDDM-EXPAND PROTO3_TEST_SERIALIZE_OPTIONAL_FIELDS()
+// This block of code is generated, do not edit it directly.
+// clang-format off
+
+ { // optionalInt32
+ Message3Optional *msg = [[Message3Optional alloc] init];
+ NSData *data = [msg data];
+ XCTAssertEqual([data length], 0U);
+ msg.optionalInt32 = 0;
+ data = [msg data];
+ XCTAssertEqual(data.length, 2);
+ NSError *err = nil;
+ Message3Optional *msg2 = [Message3Optional parseFromData:data error:&err];
+ XCTAssertNotNil(msg2);
+ XCTAssertNil(err);
+ XCTAssertTrue(msg2.hasOptionalInt32);
+ XCTAssertEqualObjects(msg, msg2);
+ [msg release];
+ }
+
+ { // optionalInt64
+ Message3Optional *msg = [[Message3Optional alloc] init];
+ NSData *data = [msg data];
+ XCTAssertEqual([data length], 0U);
+ msg.optionalInt64 = 0;
+ data = [msg data];
+ XCTAssertEqual(data.length, 2);
+ NSError *err = nil;
+ Message3Optional *msg2 = [Message3Optional parseFromData:data error:&err];
+ XCTAssertNotNil(msg2);
+ XCTAssertNil(err);
+ XCTAssertTrue(msg2.hasOptionalInt64);
+ XCTAssertEqualObjects(msg, msg2);
+ [msg release];
+ }
+
+ { // optionalUint32
+ Message3Optional *msg = [[Message3Optional alloc] init];
+ NSData *data = [msg data];
+ XCTAssertEqual([data length], 0U);
+ msg.optionalUint32 = 0;
+ data = [msg data];
+ XCTAssertEqual(data.length, 2);
+ NSError *err = nil;
+ Message3Optional *msg2 = [Message3Optional parseFromData:data error:&err];
+ XCTAssertNotNil(msg2);
+ XCTAssertNil(err);
+ XCTAssertTrue(msg2.hasOptionalUint32);
+ XCTAssertEqualObjects(msg, msg2);
+ [msg release];
+ }
+
+ { // optionalUint64
+ Message3Optional *msg = [[Message3Optional alloc] init];
+ NSData *data = [msg data];
+ XCTAssertEqual([data length], 0U);
+ msg.optionalUint64 = 0;
+ data = [msg data];
+ XCTAssertEqual(data.length, 2);
+ NSError *err = nil;
+ Message3Optional *msg2 = [Message3Optional parseFromData:data error:&err];
+ XCTAssertNotNil(msg2);
+ XCTAssertNil(err);
+ XCTAssertTrue(msg2.hasOptionalUint64);
+ XCTAssertEqualObjects(msg, msg2);
+ [msg release];
+ }
+
+ { // optionalSint32
+ Message3Optional *msg = [[Message3Optional alloc] init];
+ NSData *data = [msg data];
+ XCTAssertEqual([data length], 0U);
+ msg.optionalSint32 = 0;
+ data = [msg data];
+ XCTAssertEqual(data.length, 2);
+ NSError *err = nil;
+ Message3Optional *msg2 = [Message3Optional parseFromData:data error:&err];
+ XCTAssertNotNil(msg2);
+ XCTAssertNil(err);
+ XCTAssertTrue(msg2.hasOptionalSint32);
+ XCTAssertEqualObjects(msg, msg2);
+ [msg release];
+ }
+
+ { // optionalSint64
+ Message3Optional *msg = [[Message3Optional alloc] init];
+ NSData *data = [msg data];
+ XCTAssertEqual([data length], 0U);
+ msg.optionalSint64 = 0;
+ data = [msg data];
+ XCTAssertEqual(data.length, 2);
+ NSError *err = nil;
+ Message3Optional *msg2 = [Message3Optional parseFromData:data error:&err];
+ XCTAssertNotNil(msg2);
+ XCTAssertNil(err);
+ XCTAssertTrue(msg2.hasOptionalSint64);
+ XCTAssertEqualObjects(msg, msg2);
+ [msg release];
+ }
+
+ { // optionalFixed32
+ Message3Optional *msg = [[Message3Optional alloc] init];
+ NSData *data = [msg data];
+ XCTAssertEqual([data length], 0U);
+ msg.optionalFixed32 = 0;
+ data = [msg data];
+ XCTAssertEqual(data.length, 5);
+ NSError *err = nil;
+ Message3Optional *msg2 = [Message3Optional parseFromData:data error:&err];
+ XCTAssertNotNil(msg2);
+ XCTAssertNil(err);
+ XCTAssertTrue(msg2.hasOptionalFixed32);
+ XCTAssertEqualObjects(msg, msg2);
+ [msg release];
+ }
+
+ { // optionalFixed64
+ Message3Optional *msg = [[Message3Optional alloc] init];
+ NSData *data = [msg data];
+ XCTAssertEqual([data length], 0U);
+ msg.optionalFixed64 = 0;
+ data = [msg data];
+ XCTAssertEqual(data.length, 9);
+ NSError *err = nil;
+ Message3Optional *msg2 = [Message3Optional parseFromData:data error:&err];
+ XCTAssertNotNil(msg2);
+ XCTAssertNil(err);
+ XCTAssertTrue(msg2.hasOptionalFixed64);
+ XCTAssertEqualObjects(msg, msg2);
+ [msg release];
+ }
+
+ { // optionalSfixed32
+ Message3Optional *msg = [[Message3Optional alloc] init];
+ NSData *data = [msg data];
+ XCTAssertEqual([data length], 0U);
+ msg.optionalSfixed32 = 0;
+ data = [msg data];
+ XCTAssertEqual(data.length, 5);
+ NSError *err = nil;
+ Message3Optional *msg2 = [Message3Optional parseFromData:data error:&err];
+ XCTAssertNotNil(msg2);
+ XCTAssertNil(err);
+ XCTAssertTrue(msg2.hasOptionalSfixed32);
+ XCTAssertEqualObjects(msg, msg2);
+ [msg release];
+ }
+
+ { // optionalSfixed64
+ Message3Optional *msg = [[Message3Optional alloc] init];
+ NSData *data = [msg data];
+ XCTAssertEqual([data length], 0U);
+ msg.optionalSfixed64 = 0;
+ data = [msg data];
+ XCTAssertEqual(data.length, 9);
+ NSError *err = nil;
+ Message3Optional *msg2 = [Message3Optional parseFromData:data error:&err];
+ XCTAssertNotNil(msg2);
+ XCTAssertNil(err);
+ XCTAssertTrue(msg2.hasOptionalSfixed64);
+ XCTAssertEqualObjects(msg, msg2);
+ [msg release];
+ }
+
+ { // optionalFloat
+ Message3Optional *msg = [[Message3Optional alloc] init];
+ NSData *data = [msg data];
+ XCTAssertEqual([data length], 0U);
+ msg.optionalFloat = 0.0f;
+ data = [msg data];
+ XCTAssertEqual(data.length, 5);
+ NSError *err = nil;
+ Message3Optional *msg2 = [Message3Optional parseFromData:data error:&err];
+ XCTAssertNotNil(msg2);
+ XCTAssertNil(err);
+ XCTAssertTrue(msg2.hasOptionalFloat);
+ XCTAssertEqualObjects(msg, msg2);
+ [msg release];
+ }
+
+ { // optionalDouble
+ Message3Optional *msg = [[Message3Optional alloc] init];
+ NSData *data = [msg data];
+ XCTAssertEqual([data length], 0U);
+ msg.optionalDouble = 0.0;
+ data = [msg data];
+ XCTAssertEqual(data.length, 9);
+ NSError *err = nil;
+ Message3Optional *msg2 = [Message3Optional parseFromData:data error:&err];
+ XCTAssertNotNil(msg2);
+ XCTAssertNil(err);
+ XCTAssertTrue(msg2.hasOptionalDouble);
+ XCTAssertEqualObjects(msg, msg2);
+ [msg release];
+ }
+
+ { // optionalBool
+ Message3Optional *msg = [[Message3Optional alloc] init];
+ NSData *data = [msg data];
+ XCTAssertEqual([data length], 0U);
+ msg.optionalBool = NO;
+ data = [msg data];
+ XCTAssertEqual(data.length, 2);
+ NSError *err = nil;
+ Message3Optional *msg2 = [Message3Optional parseFromData:data error:&err];
+ XCTAssertNotNil(msg2);
+ XCTAssertNil(err);
+ XCTAssertTrue(msg2.hasOptionalBool);
+ XCTAssertEqualObjects(msg, msg2);
+ [msg release];
+ }
+
+ { // optionalString
+ Message3Optional *msg = [[Message3Optional alloc] init];
+ NSData *data = [msg data];
+ XCTAssertEqual([data length], 0U);
+ msg.optionalString = @"";
+ data = [msg data];
+ XCTAssertEqual(data.length, 2);
+ NSError *err = nil;
+ Message3Optional *msg2 = [Message3Optional parseFromData:data error:&err];
+ XCTAssertNotNil(msg2);
+ XCTAssertNil(err);
+ XCTAssertTrue(msg2.hasOptionalString);
+ XCTAssertEqualObjects(msg, msg2);
+ [msg release];
+ }
+
+ { // optionalBytes
+ Message3Optional *msg = [[Message3Optional alloc] init];
+ NSData *data = [msg data];
+ XCTAssertEqual([data length], 0U);
+ msg.optionalBytes = [NSData data];
+ data = [msg data];
+ XCTAssertEqual(data.length, 2);
+ NSError *err = nil;
+ Message3Optional *msg2 = [Message3Optional parseFromData:data error:&err];
+ XCTAssertNotNil(msg2);
+ XCTAssertNil(err);
+ XCTAssertTrue(msg2.hasOptionalBytes);
+ XCTAssertEqualObjects(msg, msg2);
+ [msg release];
+ }
+
+ //
+ // Test doesn't apply to optionalMessage (no groups in proto3).
+ //
+
+ { // optionalEnum
+ Message3Optional *msg = [[Message3Optional alloc] init];
+ NSData *data = [msg data];
+ XCTAssertEqual([data length], 0U);
+ msg.optionalEnum = Message3Optional_Enum_Foo;
+ data = [msg data];
+ XCTAssertEqual(data.length, 3);
+ NSError *err = nil;
+ Message3Optional *msg2 = [Message3Optional parseFromData:data error:&err];
+ XCTAssertNotNil(msg2);
+ XCTAssertNil(err);
+ XCTAssertTrue(msg2.hasOptionalEnum);
+ XCTAssertEqualObjects(msg, msg2);
+ [msg release];
+ }
+
+// clang-format on
+//%PDDM-EXPAND-END PROTO3_TEST_SERIALIZE_OPTIONAL_FIELDS()
+}
+
- (void)testProto2UnknownEnumToUnknownField {
Message3 *orig = [[Message3 alloc] init];
diff --git a/objectivec/Tests/unittest_runtime_proto3.proto b/objectivec/Tests/unittest_runtime_proto3.proto
index ad2e362..c2ee5fb 100644
--- a/objectivec/Tests/unittest_runtime_proto3.proto
+++ b/objectivec/Tests/unittest_runtime_proto3.proto
@@ -119,3 +119,31 @@
map<int32 , Enum > map_int32_enum = 87;
map<int32 , Message3> map_int32_message = 88;
}
+
+message Message3Optional {
+ enum Enum {
+ FOO = 0;
+ BAR = 1;
+ BAZ = 2;
+ EXTRA_3 = 30;
+ }
+
+ optional int32 optional_int32 = 1;
+ optional int64 optional_int64 = 2;
+ optional uint32 optional_uint32 = 3;
+ optional uint64 optional_uint64 = 4;
+ optional sint32 optional_sint32 = 5;
+ optional sint64 optional_sint64 = 6;
+ optional fixed32 optional_fixed32 = 7;
+ optional fixed64 optional_fixed64 = 8;
+ optional sfixed32 optional_sfixed32 = 9;
+ optional sfixed64 optional_sfixed64 = 10;
+ optional float optional_float = 11;
+ optional double optional_double = 12;
+ optional bool optional_bool = 13;
+ optional string optional_string = 14;
+ optional bytes optional_bytes = 15;
+ // No 'group' in proto3.
+ optional Message3 optional_message = 18;
+ optional Enum optional_enum = 19;
+}