diff --git a/formats/protobuf/jvmTest/src/kotlinx/serialization/protobuf/conformance/Proto3EnumTest.kt b/formats/protobuf/jvmTest/src/kotlinx/serialization/protobuf/conformance/Proto3EnumTest.kt new file mode 100644 index 000000000..7b2dda22a --- /dev/null +++ b/formats/protobuf/jvmTest/src/kotlinx/serialization/protobuf/conformance/Proto3EnumTest.kt @@ -0,0 +1,90 @@ +/* + * Copyright 2017-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.protobuf.conformance + +import com.google.protobuf_test_messages.proto3.* +import kotlinx.serialization.* +import kotlinx.serialization.protobuf.* +import kotlin.test.* + +@Serializable +data class KTestMessagesProto3Enum( + @ProtoNumber(21) val optionalNestedEnum: KNestedEnum = KNestedEnum.FOO, + @ProtoNumber(22) val optionalForeignEnum: KForeignEnum = KForeignEnum.FOREIGN_FOO, + @ProtoNumber(23) val optionalAliasedEnum: KAliasedEnum = KAliasedEnum.ALIAS_FOO, +) { + enum class KNestedEnum { + @ProtoNumber(0) + FOO, + + @ProtoNumber(1) + BAR, + + @ProtoNumber(2) + BAZ, + + @ProtoNumber(-1) + NEG; + + fun toProto() = TestMessagesProto3.TestAllTypesProto3.NestedEnum.valueOf(this.name) + } + + + enum class KAliasedEnum { + @ProtoNumber(0) + ALIAS_FOO, + + @ProtoNumber(1) + ALIAS_BAR, + + @ProtoNumber(2) + ALIAS_BAZ, + + @ProtoNumber(2) + MOO, + + @ProtoNumber(2) + moo, + + @ProtoNumber(2) + bAz; + + fun toProto() = TestMessagesProto3.TestAllTypesProto3.AliasedEnum.valueOf(this.name) + } +} + +enum class KForeignEnum { + @ProtoNumber(0) + FOREIGN_FOO, + + @ProtoNumber(1) + FOREIGN_BAR, + + @ProtoNumber(2) + FOREIGN_BAZ; + + fun toProto() = TestMessagesProto3.ForeignEnum.valueOf(this.name) +} + +class Proto3EnumTest { + @Test + fun default() { + val message = KTestMessagesProto3Enum( + optionalNestedEnum = KTestMessagesProto3Enum.KNestedEnum.NEG, + optionalForeignEnum = KForeignEnum.FOREIGN_BAR, + optionalAliasedEnum = KTestMessagesProto3Enum.KAliasedEnum.ALIAS_BAR + ) + + val bytes = ProtoBuf.encodeToByteArray(message) + val restored = TestMessagesProto3.TestAllTypesProto3.parseFrom(bytes) + + assertEquals(message.optionalNestedEnum.toProto(), restored.optionalNestedEnum) + assertEquals(message.optionalForeignEnum.toProto(), restored.optionalForeignEnum) + assertEquals(message.optionalAliasedEnum.toProto(), restored.optionalAliasedEnum) + + val restoredMessage = ProtoBuf.decodeFromByteArray(restored.toByteArray()) + assertEquals(message, restoredMessage) + } +} diff --git a/formats/protobuf/jvmTest/src/kotlinx/serialization/protobuf/conformance/Proto3MapTest.kt b/formats/protobuf/jvmTest/src/kotlinx/serialization/protobuf/conformance/Proto3MapTest.kt new file mode 100644 index 000000000..a96142492 --- /dev/null +++ b/formats/protobuf/jvmTest/src/kotlinx/serialization/protobuf/conformance/Proto3MapTest.kt @@ -0,0 +1,154 @@ +/* + * Copyright 2017-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.protobuf.conformance + +import com.google.protobuf_test_messages.proto3.* +import io.kotlintest.properties.* +import kotlinx.serialization.* +import kotlinx.serialization.protobuf.* +import kotlin.test.* + +@Serializable +data class KTestMessagesProto3Map( + @ProtoNumber(56) val mapInt32Int32: Map = emptyMap(), + @ProtoNumber(57) val mapInt64Int64: Map = emptyMap(), + @ProtoNumber(58) val mapUint32Uint32: Map = emptyMap(), + @ProtoNumber(59) val mapUint64Uint64: Map = emptyMap(), + @ProtoNumber(60) val mapSint32Sint32: Map = emptyMap(), + @ProtoNumber(61) val mapSint64Sint64: Map = emptyMap(), + @ProtoNumber(62) val mapFixed32Fixed32: Map = emptyMap(), + @ProtoNumber(63) val mapFixed64Fixed64: Map = emptyMap(), + @ProtoNumber(64) val mapSfixed32Sfixed32: Map = emptyMap(), + @ProtoNumber(65) val mapSfixed64Sfixed64: Map = emptyMap(), + @ProtoNumber(66) val mapInt32Float: Map = emptyMap(), + @ProtoNumber(67) val mapInt32Double: Map = emptyMap(), + @ProtoNumber(68) val mapBoolBool: Map = emptyMap(), + @ProtoNumber(69) val mapStringString: Map = emptyMap(), + @ProtoNumber(70) val mapStringBytes: Map = emptyMap(), + @ProtoNumber(71) val mapStringNestedMessage: Map = emptyMap(), + @ProtoNumber(72) val mapStringForeignMessage: Map = emptyMap(), + @ProtoNumber(73) val mapStringNestedEnum: Map = emptyMap(), + @ProtoNumber(74) val mapStringForeignEnum: Map = emptyMap(), +) + +class Proto3MapTest { + @Test + fun default() { + val message = KTestMessagesProto3Map( + mapInt32Int32 = Gen.map(Gen.int(), Gen.int()).generate(), + mapInt64Int64 = Gen.map(Gen.long(), Gen.long()).generate(), + mapUint32Uint32 = Gen.map(Gen.int().map { it.toUInt() }, Gen.int().map { it.toUInt() }).generate(), + mapUint64Uint64 = Gen.map(Gen.int().map { it.toULong() }, Gen.int().map { it.toULong() }).generate(), + mapInt32Float = Gen.map(Gen.int(), Gen.float()).generate(), + mapInt32Double = Gen.map(Gen.int(), Gen.double()).generate(), + mapBoolBool = Gen.map(Gen.bool(), Gen.bool()).generate(), + mapStringString = Gen.map(Gen.string(), Gen.string()).generate(), + mapStringBytes = Gen.map(Gen.string(), Gen.string().map { it.toByteArray() }).generate(), + mapStringNestedMessage = mapOf( + "asd_1" to KTestMessagesProto3Message.KNestedMessage( + 1, + null + ), + "asi_#" to KTestMessagesProto3Message.KNestedMessage( + 2, + KTestMessagesProto3Message( + KTestMessagesProto3Message.KNestedMessage(3, null), + ) + ) + ), + mapStringForeignMessage = mapOf( + "" to KForeignMessage(1), + "-2" to KForeignMessage(-12), + ), + mapStringNestedEnum = Gen.map( + Gen.string(), Gen.oneOf( + KTestMessagesProto3Enum.KNestedEnum.entries, + ) + ).generate(), + mapStringForeignEnum = Gen.map( + Gen.string(), Gen.oneOf( + KForeignEnum.entries, + ) + ).generate(), + ) + + val bytes = ProtoBuf.encodeToByteArray(message) + val restored = TestMessagesProto3.TestAllTypesProto3.parseFrom(bytes) + + + assertEquals(message.mapInt32Int32, restored.mapInt32Int32Map) + assertEquals(message.mapInt64Int64, restored.mapInt64Int64Map) + assertEquals( + message.mapUint32Uint32, + restored.mapUint32Uint32Map.map { it.key.toUInt() to it.value.toUInt() }.toMap() + ) + assertEquals( + message.mapUint64Uint64, + restored.mapUint64Uint64Map.map { it.key.toULong() to it.value.toULong() }.toMap() + ) + assertEquals(message.mapInt32Float, restored.mapInt32FloatMap) + assertEquals(message.mapInt32Double, restored.mapInt32DoubleMap) + assertEquals(message.mapBoolBool, restored.mapBoolBoolMap) + assertEquals(message.mapStringString, restored.mapStringStringMap) + assertContentEquals( + message.mapStringBytes.mapValues { it.value.toString(Charsets.UTF_32) }.entries.toList(), + restored.mapStringBytesMap.mapValues { it.value.toByteArray().toString(Charsets.UTF_32) }.entries.toList() + ) + assertEquals( + message.mapStringNestedMessage.mapValues { it.value.toProto() }, + restored.mapStringNestedMessageMap + ) + assertEquals( + message.mapStringForeignMessage.mapValues { it.value.toProto() }, + restored.mapStringForeignMessageMap + ) + assertEquals( + message.mapStringNestedEnum.mapValues { it.value.name }, + restored.mapStringNestedEnumMap.mapValues { it.value.name }, + ) + assertEquals( + message.mapStringForeignEnum.mapValues { it.value.name }, + restored.mapStringForeignEnumMap.mapValues { it.value.name } + ) + + val restoredMessage = ProtoBuf.decodeFromByteArray(restored.toByteArray()) + assertEquals(message.copy(mapStringBytes = mapOf()), restoredMessage.copy(mapStringBytes = mapOf())) + } + + @Test + @Ignore + // Issue: https://github.com/Kotlin/kotlinx.serialization/issues/2417 + fun signedAndFixed() { + val message = KTestMessagesProto3Map( + mapSint32Sint32 = Gen.map(Gen.int(), Gen.int()).generate(), + mapSint64Sint64 = Gen.map(Gen.long(), Gen.long()).generate(), + mapFixed32Fixed32 = Gen.map(Gen.int(), Gen.int()).generate(), + mapFixed64Fixed64 = Gen.map(Gen.long(), Gen.long()).generate(), + mapSfixed32Sfixed32 = Gen.map(Gen.int(), Gen.int()).generate(), + mapSfixed64Sfixed64 = Gen.map(Gen.long(), Gen.long()).generate(), + ) + + val bytes = ProtoBuf.encodeToByteArray(message) + val restored = TestMessagesProto3.TestAllTypesProto3.parseFrom(bytes) + + + assertContentEquals(message.mapSint32Sint32.entries.toList(), restored.mapSint32Sint32Map.entries.toList()) + assertContentEquals(message.mapSint64Sint64.entries.toList(), restored.mapSint64Sint64Map.entries.toList()) + assertContentEquals(message.mapFixed32Fixed32.entries.toList(), restored.mapFixed32Fixed32Map.entries.toList()) + assertContentEquals(message.mapFixed64Fixed64.entries.toList(), restored.mapFixed64Fixed64Map.entries.toList()) + assertContentEquals( + message.mapSfixed32Sfixed32.entries.toList(), + restored.mapSfixed32Sfixed32Map.entries.toList() + ) + assertContentEquals( + message.mapSfixed64Sfixed64.entries.toList(), + restored.mapSfixed64Sfixed64Map.entries.toList() + ) + + + val restoredMessage = ProtoBuf.decodeFromByteArray(restored.toByteArray()) + assertEquals(message, restoredMessage) + } +} diff --git a/formats/protobuf/jvmTest/src/kotlinx/serialization/protobuf/conformance/Proto3MessageTest.kt b/formats/protobuf/jvmTest/src/kotlinx/serialization/protobuf/conformance/Proto3MessageTest.kt new file mode 100644 index 000000000..c369d6ebb --- /dev/null +++ b/formats/protobuf/jvmTest/src/kotlinx/serialization/protobuf/conformance/Proto3MessageTest.kt @@ -0,0 +1,65 @@ +/* + * Copyright 2017-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.protobuf.conformance + +import com.google.protobuf_test_messages.proto3.* +import kotlinx.serialization.* +import kotlinx.serialization.protobuf.* +import kotlin.test.* + +@Serializable +data class KTestMessagesProto3Message( + @ProtoNumber(18) val optionalNestedMessage: KNestedMessage? = null, + @ProtoNumber(19) val optionalForeignMessage: KForeignMessage? = null, +) { + @Serializable + data class KNestedMessage( + @ProtoNumber(1) val a: Int = 0, + @ProtoNumber(2) val corecursive: KTestMessagesProto3Message? = null, + ) { + fun toProto(): TestMessagesProto3.TestAllTypesProto3.NestedMessage = + TestMessagesProto3.TestAllTypesProto3.NestedMessage.parseFrom( + ProtoBuf.encodeToByteArray(this) + ) + } +} + +@Serializable +data class KForeignMessage( + @ProtoNumber(1) val c: Int = 0, +) { + fun toProto(): TestMessagesProto3.ForeignMessage = + TestMessagesProto3.ForeignMessage.parseFrom( + ProtoBuf.encodeToByteArray(this) + ) +} + +class Proto3MessageTest { + @Test + fun default() { + val message = KTestMessagesProto3Message( + optionalNestedMessage = KTestMessagesProto3Message.KNestedMessage( + a = 150, + corecursive = KTestMessagesProto3Message( + optionalNestedMessage = KTestMessagesProto3Message.KNestedMessage( + a = 42, + ) + ) + ), + optionalForeignMessage = KForeignMessage( + c = 150, + ) + ) + + val bytes = ProtoBuf.encodeToByteArray(message) + val restored = TestMessagesProto3.TestAllTypesProto3.parseFrom(bytes) + assertEquals(message.optionalNestedMessage?.a, restored.optionalNestedMessage.a) + assertEquals( + message.optionalNestedMessage?.corecursive?.optionalNestedMessage?.a, + restored.optionalNestedMessage.corecursive.optionalNestedMessage.a + ) + assertEquals(message.optionalForeignMessage?.c, restored.optionalForeignMessage.c) + } +} diff --git a/formats/protobuf/jvmTest/src/kotlinx/serialization/protobuf/conformance/Proto3OneofTest.kt b/formats/protobuf/jvmTest/src/kotlinx/serialization/protobuf/conformance/Proto3OneofTest.kt new file mode 100644 index 000000000..fda811eac --- /dev/null +++ b/formats/protobuf/jvmTest/src/kotlinx/serialization/protobuf/conformance/Proto3OneofTest.kt @@ -0,0 +1,131 @@ +/* + * Copyright 2017-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.protobuf.conformance + +import com.google.protobuf_test_messages.proto3.* +import kotlinx.serialization.* +import kotlinx.serialization.protobuf.* +import kotlin.test.* + +@Serializable +data class KTestMessageProto3Oneof( + @ProtoNumber(111) val oneofUint32: UInt? = null, + @ProtoNumber(112) val oneofNestedMessage: KTestMessagesProto3Message.KNestedMessage? = null, + @ProtoNumber(113) val oneofString: String? = null, + @ProtoNumber(114) val oneofBytes: ByteArray? = null, + @ProtoNumber(115) val oneofBool: Boolean? = null, + @ProtoNumber(116) val oneofUint64: ULong? = null, + @ProtoNumber(117) val oneofFloat: Float? = null, + @ProtoNumber(118) val oneofDouble: Double? = null, + @ProtoNumber(119) val oneofEnum: KTestMessagesProto3Enum.KNestedEnum? = null, +) { + init { + require( + listOf( + oneofUint32, + oneofNestedMessage, + oneofString, + oneofBytes, + oneofBool, + oneofUint64, + oneofFloat, + oneofDouble, + oneofEnum, + ).count { it != null } == 1 + ) + } +} + +class Proto3OneofTest { + + /** + * Verify that the given [KTestMessageProto3Oneof] is correctly encoded and decoded as + * [TestMessagesProto3.TestAllTypesProto3] by running the [verificationFunction]. This + * method also verifies that the encoded and decoded message is equal to the original message. + * + * @param verificationFunction a function that verifies the encoded and decoded message. First parameter + * is the original message and the second parameter is the decoded protobuf library message. + * @receiver the [KTestMessageProto3Oneof] to verify + */ + private fun KTestMessageProto3Oneof.verify( + verificationFunction: (KTestMessageProto3Oneof, TestMessagesProto3.TestAllTypesProto3) -> Unit, + ) { + val bytes = ProtoBuf.encodeToByteArray(this) + val restored = TestMessagesProto3.TestAllTypesProto3.parseFrom(bytes) + + verificationFunction.invoke(this, restored) + + val restoredMessage = ProtoBuf.decodeFromByteArray(restored.toByteArray()) + + // [equals] method is not implemented for [ByteArray] so we need to compare it separately. + assertEquals(this, restoredMessage.copy(oneofBytes = this.oneofBytes)) + assertContentEquals(this.oneofBytes, restoredMessage.oneofBytes) + } + + @Test + fun uint32() { + KTestMessageProto3Oneof(oneofUint32 = 150u).verify { self, restored -> + assertEquals(self.oneofUint32, restored.oneofUint32.toUInt()) + } + } + + @Test + fun nestedMessage() { + KTestMessageProto3Oneof( + oneofNestedMessage = KTestMessagesProto3Message.KNestedMessage(a = 150), + ).verify { self, restored -> + assertEquals(self.oneofNestedMessage?.a, restored.oneofNestedMessage.a) + } + } + + @Test + fun string() { + KTestMessageProto3Oneof(oneofString = "150").verify { self, restored -> + assertEquals(self.oneofString, restored.oneofString) + } + } + + @Test + fun bytes() { + KTestMessageProto3Oneof(oneofBytes = "150".toByteArray()).verify { self, restored -> + assertContentEquals(self.oneofBytes, restored.oneofBytes.toByteArray()) + } + } + + @Test + fun bool() { + KTestMessageProto3Oneof(oneofBool = true).verify { self, restored -> + assertEquals(self.oneofBool, restored.oneofBool) + } + } + + @Test + fun uint64() { + KTestMessageProto3Oneof(oneofUint64 = 150uL).verify { self, restored -> + assertEquals(self.oneofUint64, restored.oneofUint64.toULong()) + } + } + + @Test + fun float() { + KTestMessageProto3Oneof(oneofFloat = 150f).verify { self, restored -> + assertEquals(self.oneofFloat, restored.oneofFloat) + } + } + + @Test + fun double() { + KTestMessageProto3Oneof(oneofDouble = 150.0).verify { self, restored -> + assertEquals(self.oneofDouble, restored.oneofDouble) + } + } + + @Test + fun enum() { + KTestMessageProto3Oneof(oneofEnum = KTestMessagesProto3Enum.KNestedEnum.BAR).verify { self, restored -> + assertEquals(self.oneofEnum?.name, restored.oneofEnum.name) + } + } +} diff --git a/formats/protobuf/jvmTest/src/kotlinx/serialization/protobuf/conformance/Proto3PackedTest.kt b/formats/protobuf/jvmTest/src/kotlinx/serialization/protobuf/conformance/Proto3PackedTest.kt new file mode 100644 index 000000000..e0da0bb8f --- /dev/null +++ b/formats/protobuf/jvmTest/src/kotlinx/serialization/protobuf/conformance/Proto3PackedTest.kt @@ -0,0 +1,99 @@ +/* + * Copyright 2017-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.protobuf.conformance + +import com.google.protobuf_test_messages.proto3.* +import io.kotlintest.properties.* +import kotlinx.serialization.* +import kotlinx.serialization.protobuf.* +import kotlin.test.* + +@Serializable +data class KTestMessagesProto3Packed( + @ProtoNumber(75) @ProtoPacked val packedInt32: List = emptyList(), + @ProtoNumber(76) @ProtoPacked val packedInt64: List = emptyList(), + @ProtoNumber(77) @ProtoPacked val packedUint32: List = emptyList(), + @ProtoNumber(78) @ProtoPacked val packedUint64: List = emptyList(), + @ProtoNumber(79) @ProtoPacked val packedSint32: List = emptyList(), + @ProtoNumber(80) @ProtoPacked val packedSint64: List = emptyList(), + @ProtoNumber(81) @ProtoPacked val packedFixed32: List = emptyList(), + @ProtoNumber(82) @ProtoPacked val packedFixed64: List = emptyList(), + @ProtoNumber(83) @ProtoPacked val packedSfixed32: List = emptyList(), + @ProtoNumber(84) @ProtoPacked val packedSfixed64: List = emptyList(), + @ProtoNumber(85) @ProtoPacked val packedFloat: List = emptyList(), + @ProtoNumber(86) @ProtoPacked val packedDouble: List = emptyList(), + @ProtoNumber(87) @ProtoPacked val packedBool: List = emptyList(), +) + +class Proto3PackedTest { + @Test + fun default() { + val message = KTestMessagesProto3Packed( + packedInt32 = Gen.list(Gen.int()).generate(), + packedInt64 = Gen.list(Gen.long()).generate(), + packedFloat = Gen.list(Gen.float()).generate(), + packedDouble = Gen.list(Gen.double()).generate(), + packedBool = Gen.list(Gen.bool()).generate(), + ) + + val bytes = ProtoBuf.encodeToByteArray(message) + val restored = TestMessagesProto3.TestAllTypesProto3.parseFrom(bytes) + + assertEquals(message.packedInt32, restored.packedInt32List) + assertEquals(message.packedInt64, restored.packedInt64List) + assertEquals(message.packedFloat, restored.packedFloatList) + assertEquals(message.packedDouble, restored.packedDoubleList) + assertEquals(message.packedBool, restored.packedBoolList) + + val restoredMessage = ProtoBuf.decodeFromByteArray(restored.toByteArray()) + assertEquals(message, restoredMessage) + } + + @Test + @Ignore + // Issue: https://github.com/Kotlin/kotlinx.serialization/issues/2419 + fun signedAndFixed() { + val message = KTestMessagesProto3Packed( + packedSint32 = Gen.list(Gen.int()).generate(), + packedSint64 = Gen.list(Gen.long()).generate(), + packedFixed32 = Gen.list(Gen.int()).generate(), + packedFixed64 = Gen.list(Gen.long()).generate(), + packedSfixed32 = Gen.list(Gen.int()).generate(), + packedSfixed64 = Gen.list(Gen.long()).generate(), + ) + + val bytes = ProtoBuf.encodeToByteArray(message) + val restored = TestMessagesProto3.TestAllTypesProto3.parseFrom(bytes) + + assertEquals(message.packedSint32, restored.packedSint32List) + assertEquals(message.packedSint64, restored.packedSint64List) + assertEquals(message.packedFixed32, restored.packedFixed32List) + assertEquals(message.packedFixed64, restored.packedFixed64List) + assertEquals(message.packedSfixed32, restored.packedSfixed32List) + assertEquals(message.packedSfixed64, restored.packedSfixed64List) + + val restoredMessage = ProtoBuf.decodeFromByteArray(restored.toByteArray()) + assertEquals(message, restoredMessage) + } + + @Test + @Ignore + // Issue: https://github.com/Kotlin/kotlinx.serialization/issues/2418 + fun unsigned() { + val message = KTestMessagesProto3Packed( + packedUint32 = Gen.list(Gen.int().map { it.toUInt() }).generate(), + packedUint64 = Gen.list(Gen.long().map { it.toULong() }).generate(), + ) + + val bytes = ProtoBuf.encodeToByteArray(message) + val restored = TestMessagesProto3.TestAllTypesProto3.parseFrom(bytes) + + assertEquals(message.packedUint32, restored.packedUint32List.map { it.toUInt() }) + assertEquals(message.packedUint64, restored.packedUint64List.map { it.toULong() }) + + val restoredMessage = ProtoBuf.decodeFromByteArray(restored.toByteArray()) + assertEquals(message, restoredMessage) + } +} diff --git a/formats/protobuf/jvmTest/src/kotlinx/serialization/protobuf/conformance/Proto3PrimitiveTest.kt b/formats/protobuf/jvmTest/src/kotlinx/serialization/protobuf/conformance/Proto3PrimitiveTest.kt new file mode 100644 index 000000000..a7363f8f8 --- /dev/null +++ b/formats/protobuf/jvmTest/src/kotlinx/serialization/protobuf/conformance/Proto3PrimitiveTest.kt @@ -0,0 +1,77 @@ +/* + * Copyright 2017-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.protobuf.conformance + +import com.google.protobuf_test_messages.proto3.* +import kotlinx.serialization.* +import kotlinx.serialization.protobuf.* +import kotlin.test.* + +@Serializable +data class KTestMessagesProto3Primitive( + @ProtoNumber(1) val optionalInt32: Int = 0, + @ProtoNumber(2) val optionalInt64: Long = 0, + @ProtoNumber(3) val optionalUint32: UInt = 0U, + @ProtoNumber(4) val optionalUint64: ULong = 0UL, + @ProtoNumber(5) @ProtoType(ProtoIntegerType.SIGNED) val optionalSint32: Int = 0, + @ProtoNumber(6) @ProtoType(ProtoIntegerType.SIGNED) val optionalSint64: Long = 0, + @ProtoNumber(7) @ProtoType(ProtoIntegerType.FIXED) val optionalFixed32: Int = 0, + @ProtoNumber(8) @ProtoType(ProtoIntegerType.FIXED) val optionalFixed64: Long = 0, + @ProtoNumber(9) @ProtoType(ProtoIntegerType.FIXED) val optionalSfixed32: Int = 0, + @ProtoNumber(10) @ProtoType(ProtoIntegerType.FIXED) val optionalSfixed64: Long = 0, + @ProtoNumber(11) val optionalFloat: Float = 0.0f, + @ProtoNumber(12) val optionalDouble: Double = 0.0, + @ProtoNumber(13) val optionalBool: Boolean = false, + @ProtoNumber(14) val optionalString: String = "", + @ProtoNumber(15) val optionalBytes: ByteArray = byteArrayOf(), +) + +class Proto3PrimitiveTest { + @Test + fun default() { + val message = KTestMessagesProto3Primitive( + optionalInt32 = Int.MAX_VALUE, + optionalInt64 = Long.MAX_VALUE, + optionalUint32 = UInt.MAX_VALUE, + optionalUint64 = ULong.MAX_VALUE, + optionalSint32 = Int.MAX_VALUE, + optionalSint64 = Long.MAX_VALUE, + optionalFixed32 = Int.MAX_VALUE, + optionalFixed64 = Long.MAX_VALUE, + optionalSfixed32 = Int.MAX_VALUE, + optionalSfixed64 = Long.MAX_VALUE, + optionalFloat = Float.MAX_VALUE, + optionalDouble = Double.MAX_VALUE, + optionalBool = true, + optionalString = "string", + optionalBytes = byteArrayOf(1, 2, 3, 4, 5) + ) + + val bytes = ProtoBuf.encodeToByteArray(message) + val restored = TestMessagesProto3.TestAllTypesProto3.parseFrom(bytes) + + assertEquals(message.optionalInt32, restored.optionalInt32) + assertEquals(message.optionalInt64, restored.optionalInt64) + assertEquals(message.optionalUint32, restored.optionalUint32.toUInt()) + assertEquals(message.optionalUint64, restored.optionalUint64.toULong()) + assertEquals(message.optionalSint32, restored.optionalSint32) + assertEquals(message.optionalSint64, restored.optionalSint64) + assertEquals(message.optionalFixed32, restored.optionalFixed32) + assertEquals(message.optionalFixed64, restored.optionalFixed64) + assertEquals(message.optionalSfixed32, restored.optionalSfixed32) + assertEquals(message.optionalSfixed64, restored.optionalSfixed64) + assertEquals(message.optionalFloat, restored.optionalFloat) + assertEquals(message.optionalDouble, restored.optionalDouble) + assertEquals(message.optionalBool, restored.optionalBool) + assertEquals(message.optionalString, restored.optionalString) + assertContentEquals(message.optionalBytes, restored.optionalBytes.toByteArray()) + + val restoredMessage = ProtoBuf.decodeFromByteArray(restored.toByteArray()) + + // [equals] method is not implemented for [ByteArray] so we need to compare it separately. + assertEquals(message, restoredMessage.copy(optionalBytes = message.optionalBytes)) + assertContentEquals(message.optionalBytes, restoredMessage.optionalBytes) + } +} diff --git a/formats/protobuf/jvmTest/src/kotlinx/serialization/protobuf/conformance/Proto3RepeatedTest.kt b/formats/protobuf/jvmTest/src/kotlinx/serialization/protobuf/conformance/Proto3RepeatedTest.kt new file mode 100644 index 000000000..b3dab8c7a --- /dev/null +++ b/formats/protobuf/jvmTest/src/kotlinx/serialization/protobuf/conformance/Proto3RepeatedTest.kt @@ -0,0 +1,133 @@ +/* + * Copyright 2017-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.protobuf.conformance + +import com.google.protobuf_test_messages.proto3.* +import io.kotlintest.properties.* +import kotlinx.serialization.* +import kotlinx.serialization.protobuf.* +import kotlin.test.* + +@Serializable +data class KTestMessagesProto3Repeated( + @ProtoNumber(31) @ProtoPacked val repeatedInt32: List = emptyList(), + @ProtoNumber(32) @ProtoPacked val repeatedInt64: List = emptyList(), + @ProtoNumber(33) @ProtoPacked val repeatedUint32: List = emptyList(), + @ProtoNumber(34) @ProtoPacked val repeatedUint64: List = emptyList(), + @ProtoNumber(35) @ProtoPacked val repeatedSint32: List = emptyList(), + @ProtoNumber(36) @ProtoPacked val repeatedSint64: List = emptyList(), + @ProtoNumber(37) @ProtoPacked val repeatedFixed32: List = emptyList(), + @ProtoNumber(38) @ProtoPacked val repeatedFixed64: List = emptyList(), + @ProtoNumber(39) @ProtoPacked val repeatedSfixed32: List = emptyList(), + @ProtoNumber(40) @ProtoPacked val repeatedSfixed64: List = emptyList(), + @ProtoNumber(41) @ProtoPacked val repeatedFloat: List = emptyList(), + @ProtoNumber(42) @ProtoPacked val repeatedDouble: List = emptyList(), + @ProtoNumber(43) @ProtoPacked val repeatedBool: List = emptyList(), + @ProtoNumber(44) val repeatedString: List = emptyList(), + @ProtoNumber(45) val repeatedBytes: List = emptyList(), + @ProtoNumber(48) val repeatedNestedMessages: List = emptyList(), + @ProtoNumber(49) val repeatedForeignMessages: List = emptyList(), +) + +class Proto3RepeatedTest { + @Test + fun default() { + val message = KTestMessagesProto3Repeated( + repeatedInt32 = Gen.list(Gen.int()).generate(), + repeatedInt64 = Gen.list(Gen.long()).generate(), + repeatedFloat = Gen.list(Gen.float()).generate(), + repeatedDouble = Gen.list(Gen.double()).generate(), + repeatedBool = Gen.list(Gen.bool()).generate(), + repeatedString = Gen.list(Gen.string()).generate(), + repeatedBytes = Gen.list(Gen.string().map { it.toByteArray() }).generate(), + repeatedNestedMessages = listOf( + KTestMessagesProto3Message.KNestedMessage( + 1, + null + ), + KTestMessagesProto3Message.KNestedMessage( + 2, + KTestMessagesProto3Message( + KTestMessagesProto3Message.KNestedMessage(3, null), + ) + ) + ), + repeatedForeignMessages = listOf( + KForeignMessage(1), + KForeignMessage(-12), + ) + ) + + val bytes = ProtoBuf.encodeToByteArray(message) + val restored = TestMessagesProto3.TestAllTypesProto3.parseFrom(bytes) + + assertEquals(message.repeatedInt32, restored.repeatedInt32List) + assertEquals(message.repeatedInt64, restored.repeatedInt64List) + assertEquals(message.repeatedFloat, restored.repeatedFloatList) + assertEquals(message.repeatedDouble, restored.repeatedDoubleList) + assertEquals(message.repeatedBool, restored.repeatedBoolList) + assertEquals(message.repeatedString, restored.repeatedStringList) + assertEquals(message.repeatedNestedMessages.map { it.toProto() }, restored.repeatedNestedMessageList) + assertEquals(message.repeatedForeignMessages.map { it.toProto() }, restored.repeatedForeignMessageList) + assertEquals(message.repeatedBytes.map { it.toList() }, restored.repeatedBytesList.map { it.toList() }) + + val restoredMessage = ProtoBuf.decodeFromByteArray(restored.toByteArray()) + // [equals] method is not implemented for [ByteArray] so we need to compare it separately. + assertEquals(message, restoredMessage.copy(repeatedBytes = message.repeatedBytes)) + assertContentEquals( + message.repeatedBytes.flatMap { it.toList() }, + restoredMessage.repeatedBytes.flatMap { it.toList() }, + ) + } + + @Test + @Ignore + // Issue: https://github.com/Kotlin/kotlinx.serialization/issues/2419 + fun signedAndFixed() { + val message = KTestMessagesProto3Repeated( + repeatedSint32 = Gen.list(Gen.int()).generate(), + repeatedSint64 = Gen.list(Gen.long()).generate(), + repeatedFixed32 = Gen.list(Gen.int()).generate(), + repeatedFixed64 = Gen.list(Gen.long()).generate(), + repeatedSfixed32 = Gen.list(Gen.int()).generate(), + repeatedSfixed64 = Gen.list(Gen.long()).generate(), + ) + + val bytes = ProtoBuf.encodeToByteArray(message) + val restored = TestMessagesProto3.TestAllTypesProto3.parseFrom(bytes) + + assertEquals(message.repeatedUint32, restored.repeatedUint32List.map { it.toUInt() }) + assertEquals(message.repeatedUint64, restored.repeatedUint64List.map { it.toULong() }) + assertEquals(message.repeatedSint32, restored.repeatedSint32List) + assertEquals(message.repeatedSint64, restored.repeatedSint64List) + assertEquals(message.repeatedFixed32, restored.repeatedFixed32List) + assertEquals(message.repeatedFixed64, restored.repeatedFixed64List) + assertEquals(message.repeatedSfixed32, restored.repeatedSfixed32List) + assertEquals(message.repeatedSfixed64, restored.repeatedSfixed64List) + + val restoredMessage = ProtoBuf.decodeFromByteArray(restored.toByteArray()) + assertEquals(message, restoredMessage) + } + + + @Test + @Ignore + // Issue: https://github.com/Kotlin/kotlinx.serialization/issues/2418 + fun unsigned() { + val message = KTestMessagesProto3Repeated( + repeatedUint32 = Gen.list(Gen.int().map { it.toUInt() }).generate(), + repeatedUint64 = Gen.list(Gen.long().map { it.toULong() }).generate(), + ) + + val bytes = ProtoBuf.encodeToByteArray(message) + val restored = TestMessagesProto3.TestAllTypesProto3.parseFrom(bytes) + + assertEquals(message.repeatedUint32, restored.repeatedUint32List.map { it.toUInt() }) + assertEquals(message.repeatedUint64, restored.repeatedUint64List.map { it.toULong() }) + + val restoredMessage = ProtoBuf.decodeFromByteArray(restored.toByteArray()) + assertEquals(message, restoredMessage) + } +} diff --git a/formats/protobuf/jvmTest/src/kotlinx/serialization/protobuf/conformance/Proto3UnpackedTest.kt b/formats/protobuf/jvmTest/src/kotlinx/serialization/protobuf/conformance/Proto3UnpackedTest.kt new file mode 100644 index 000000000..dad773d9e --- /dev/null +++ b/formats/protobuf/jvmTest/src/kotlinx/serialization/protobuf/conformance/Proto3UnpackedTest.kt @@ -0,0 +1,84 @@ +/* + * Copyright 2017-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.serialization.protobuf.conformance + +import com.google.protobuf_test_messages.proto3.* +import io.kotlintest.properties.* +import kotlinx.serialization.* +import kotlinx.serialization.protobuf.* +import kotlin.test.* + +@Serializable +data class KTestMessagesProto3Unpacked( + @ProtoNumber(89) val unpackedInt32: List = emptyList(), + @ProtoNumber(90) val unpackedInt64: List = emptyList(), + @ProtoNumber(91) val unpackedUint32: List = emptyList(), + @ProtoNumber(92) val unpackedUint64: List = emptyList(), + @ProtoNumber(93) val unpackedSint32: List = emptyList(), + @ProtoNumber(94) val unpackedSint64: List = emptyList(), + @ProtoNumber(95) val unpackedFixed32: List = emptyList(), + @ProtoNumber(96) val unpackedFixed64: List = emptyList(), + @ProtoNumber(97) val unpackedSfixed32: List = emptyList(), + @ProtoNumber(98) val unpackedSfixed64: List = emptyList(), + @ProtoNumber(99) val unpackedFloat: List = emptyList(), + @ProtoNumber(100) val unpackedDouble: List = emptyList(), + @ProtoNumber(101) val unpackedBool: List = emptyList(), +) + +class Proto3UnpackedTest { + @Test + fun default() { + val message = KTestMessagesProto3Unpacked( + unpackedInt32 = Gen.list(Gen.int()).generate(), + unpackedInt64 = Gen.list(Gen.long()).generate(), + unpackedUint32 = Gen.list(Gen.int().map { it.toUInt() }).generate(), + unpackedUint64 = Gen.list(Gen.long().map { it.toULong() }).generate(), + unpackedFloat = Gen.list(Gen.float()).generate(), + unpackedDouble = Gen.list(Gen.double()).generate(), + unpackedBool = Gen.list(Gen.bool()).generate(), + ) + + val bytes = ProtoBuf.encodeToByteArray(message) + val restored = TestMessagesProto3.TestAllTypesProto3.parseFrom(bytes) + + assertEquals(message.unpackedInt32, restored.unpackedInt32List) + assertEquals(message.unpackedInt64, restored.unpackedInt64List) + assertEquals(message.unpackedUint32, restored.unpackedUint32List.map { it.toUInt() }) + assertEquals(message.unpackedUint64, restored.unpackedUint64List.map { it.toULong() }) + assertEquals(message.unpackedFloat, restored.unpackedFloatList) + assertEquals(message.unpackedDouble, restored.unpackedDoubleList) + assertEquals(message.unpackedBool, restored.unpackedBoolList) + + val restoredMessage = ProtoBuf.decodeFromByteArray(restored.toByteArray()) + assertEquals(message, restoredMessage) + } + + @Test + @Ignore + // Issue: https://github.com/Kotlin/kotlinx.serialization/issues/2419 + fun signedAndFixed() { + val message = KTestMessagesProto3Unpacked( + unpackedSint32 = Gen.list(Gen.int()).generate(), + unpackedSint64 = Gen.list(Gen.long()).generate(), + unpackedFixed32 = Gen.list(Gen.int()).generate(), + unpackedFixed64 = Gen.list(Gen.long()).generate(), + unpackedSfixed32 = Gen.list(Gen.int()).generate(), + unpackedSfixed64 = Gen.list(Gen.long()).generate(), + ) + + val bytes = ProtoBuf.encodeToByteArray(message) + val restored = TestMessagesProto3.TestAllTypesProto3.parseFrom(bytes) + + assertEquals(message.unpackedSint32, restored.unpackedSint32List) + assertEquals(message.unpackedSint64, restored.unpackedSint64List) + assertEquals(message.unpackedFixed32, restored.unpackedFixed32List) + assertEquals(message.unpackedFixed64, restored.unpackedFixed64List) + assertEquals(message.unpackedSfixed32, restored.unpackedSfixed32List) + assertEquals(message.unpackedSfixed64, restored.unpackedSfixed64List) + + val restoredMessage = ProtoBuf.decodeFromByteArray(restored.toByteArray()) + assertEquals(message, restoredMessage) + } +} diff --git a/formats/protobuf/testProto/test_messages_proto3.proto b/formats/protobuf/testProto/test_messages_proto3.proto new file mode 100644 index 000000000..6b2799571 --- /dev/null +++ b/formats/protobuf/testProto/test_messages_proto3.proto @@ -0,0 +1,289 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 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. +// +// Test schema for proto3 messages. This test schema is used by: +// +// - benchmarks +// - fuzz tests +// - conformance tests +// +// https://github.com/protocolbuffers/protobuf/blob/5e03386555544e39c21236dca0097123edec8769/src/google/protobuf/test_messages_proto3.proto + +syntax = "proto3"; + +package protobuf_test_messages.proto3; + +option java_package = "com.google.protobuf_test_messages.proto3"; +option objc_class_prefix = "Proto3"; + +// This is the default, but we specify it here explicitly. +option optimize_for = SPEED; + +import "google/protobuf/any.proto"; +import "google/protobuf/duration.proto"; +import "google/protobuf/field_mask.proto"; +import "google/protobuf/struct.proto"; +import "google/protobuf/timestamp.proto"; +import "google/protobuf/wrappers.proto"; + +option cc_enable_arenas = true; + +// This proto includes every type of field in both singular and repeated +// forms. +// +// Also, crucially, all messages and enums in this file are eventually +// submessages of this message. So for example, a fuzz test of TestAllTypes +// could trigger bugs that occur in any message type in this file. We verify +// this stays true in a unit test. +message TestAllTypesProto3 { + message NestedMessage { + int32 a = 1; + TestAllTypesProto3 corecursive = 2; + } + + enum NestedEnum { + FOO = 0; + BAR = 1; + BAZ = 2; + NEG = -1; // Intentionally negative. + } + + enum AliasedEnum { + option allow_alias = true; + + ALIAS_FOO = 0; + ALIAS_BAR = 1; + ALIAS_BAZ = 2; + MOO = 2; + moo = 2; + bAz = 2; + } + + // Singular + int32 optional_int32 = 1; + int64 optional_int64 = 2; + uint32 optional_uint32 = 3; + uint64 optional_uint64 = 4; + sint32 optional_sint32 = 5; + sint64 optional_sint64 = 6; + fixed32 optional_fixed32 = 7; + fixed64 optional_fixed64 = 8; + sfixed32 optional_sfixed32 = 9; + sfixed64 optional_sfixed64 = 10; + float optional_float = 11; + double optional_double = 12; + bool optional_bool = 13; + string optional_string = 14; + bytes optional_bytes = 15; + + NestedMessage optional_nested_message = 18; + ForeignMessage optional_foreign_message = 19; + + NestedEnum optional_nested_enum = 21; + ForeignEnum optional_foreign_enum = 22; + AliasedEnum optional_aliased_enum = 23; + + string optional_string_piece = 24 [ctype = STRING_PIECE]; + string optional_cord = 25 [ctype = CORD]; + + TestAllTypesProto3 recursive_message = 27; + + // Repeated + repeated int32 repeated_int32 = 31; + repeated int64 repeated_int64 = 32; + repeated uint32 repeated_uint32 = 33; + repeated uint64 repeated_uint64 = 34; + repeated sint32 repeated_sint32 = 35; + repeated sint64 repeated_sint64 = 36; + repeated fixed32 repeated_fixed32 = 37; + repeated fixed64 repeated_fixed64 = 38; + repeated sfixed32 repeated_sfixed32 = 39; + repeated sfixed64 repeated_sfixed64 = 40; + repeated float repeated_float = 41; + repeated double repeated_double = 42; + repeated bool repeated_bool = 43; + repeated string repeated_string = 44; + repeated bytes repeated_bytes = 45; + + repeated NestedMessage repeated_nested_message = 48; + repeated ForeignMessage repeated_foreign_message = 49; + + repeated NestedEnum repeated_nested_enum = 51; + repeated ForeignEnum repeated_foreign_enum = 52; + + repeated string repeated_string_piece = 54 [ctype = STRING_PIECE]; + repeated string repeated_cord = 55 [ctype = CORD]; + + // Packed + repeated int32 packed_int32 = 75 [packed = true]; + repeated int64 packed_int64 = 76 [packed = true]; + repeated uint32 packed_uint32 = 77 [packed = true]; + repeated uint64 packed_uint64 = 78 [packed = true]; + repeated sint32 packed_sint32 = 79 [packed = true]; + repeated sint64 packed_sint64 = 80 [packed = true]; + repeated fixed32 packed_fixed32 = 81 [packed = true]; + repeated fixed64 packed_fixed64 = 82 [packed = true]; + repeated sfixed32 packed_sfixed32 = 83 [packed = true]; + repeated sfixed64 packed_sfixed64 = 84 [packed = true]; + repeated float packed_float = 85 [packed = true]; + repeated double packed_double = 86 [packed = true]; + repeated bool packed_bool = 87 [packed = true]; + repeated NestedEnum packed_nested_enum = 88 [packed = true]; + + // Unpacked + repeated int32 unpacked_int32 = 89 [packed = false]; + repeated int64 unpacked_int64 = 90 [packed = false]; + repeated uint32 unpacked_uint32 = 91 [packed = false]; + repeated uint64 unpacked_uint64 = 92 [packed = false]; + repeated sint32 unpacked_sint32 = 93 [packed = false]; + repeated sint64 unpacked_sint64 = 94 [packed = false]; + repeated fixed32 unpacked_fixed32 = 95 [packed = false]; + repeated fixed64 unpacked_fixed64 = 96 [packed = false]; + repeated sfixed32 unpacked_sfixed32 = 97 [packed = false]; + repeated sfixed64 unpacked_sfixed64 = 98 [packed = false]; + repeated float unpacked_float = 99 [packed = false]; + repeated double unpacked_double = 100 [packed = false]; + repeated bool unpacked_bool = 101 [packed = false]; + repeated NestedEnum unpacked_nested_enum = 102 [packed = false]; + + // Map + map map_int32_int32 = 56; + map map_int64_int64 = 57; + map map_uint32_uint32 = 58; + map map_uint64_uint64 = 59; + map map_sint32_sint32 = 60; + map map_sint64_sint64 = 61; + map map_fixed32_fixed32 = 62; + map map_fixed64_fixed64 = 63; + map map_sfixed32_sfixed32 = 64; + map map_sfixed64_sfixed64 = 65; + map map_int32_float = 66; + map map_int32_double = 67; + map map_bool_bool = 68; + map map_string_string = 69; + map map_string_bytes = 70; + map map_string_nested_message = 71; + map map_string_foreign_message = 72; + map map_string_nested_enum = 73; + map map_string_foreign_enum = 74; + + oneof oneof_field { + uint32 oneof_uint32 = 111; + NestedMessage oneof_nested_message = 112; + string oneof_string = 113; + bytes oneof_bytes = 114; + bool oneof_bool = 115; + uint64 oneof_uint64 = 116; + float oneof_float = 117; + double oneof_double = 118; + NestedEnum oneof_enum = 119; + google.protobuf.NullValue oneof_null_value = 120; + } + + // Well-known types + google.protobuf.BoolValue optional_bool_wrapper = 201; + google.protobuf.Int32Value optional_int32_wrapper = 202; + google.protobuf.Int64Value optional_int64_wrapper = 203; + google.protobuf.UInt32Value optional_uint32_wrapper = 204; + google.protobuf.UInt64Value optional_uint64_wrapper = 205; + google.protobuf.FloatValue optional_float_wrapper = 206; + google.protobuf.DoubleValue optional_double_wrapper = 207; + google.protobuf.StringValue optional_string_wrapper = 208; + google.protobuf.BytesValue optional_bytes_wrapper = 209; + + repeated google.protobuf.BoolValue repeated_bool_wrapper = 211; + repeated google.protobuf.Int32Value repeated_int32_wrapper = 212; + repeated google.protobuf.Int64Value repeated_int64_wrapper = 213; + repeated google.protobuf.UInt32Value repeated_uint32_wrapper = 214; + repeated google.protobuf.UInt64Value repeated_uint64_wrapper = 215; + repeated google.protobuf.FloatValue repeated_float_wrapper = 216; + repeated google.protobuf.DoubleValue repeated_double_wrapper = 217; + repeated google.protobuf.StringValue repeated_string_wrapper = 218; + repeated google.protobuf.BytesValue repeated_bytes_wrapper = 219; + + google.protobuf.Duration optional_duration = 301; + google.protobuf.Timestamp optional_timestamp = 302; + google.protobuf.FieldMask optional_field_mask = 303; + google.protobuf.Struct optional_struct = 304; + google.protobuf.Any optional_any = 305; + google.protobuf.Value optional_value = 306; + google.protobuf.NullValue optional_null_value = 307; + + repeated google.protobuf.Duration repeated_duration = 311; + repeated google.protobuf.Timestamp repeated_timestamp = 312; + repeated google.protobuf.FieldMask repeated_fieldmask = 313; + repeated google.protobuf.Struct repeated_struct = 324; + repeated google.protobuf.Any repeated_any = 315; + repeated google.protobuf.Value repeated_value = 316; + repeated google.protobuf.ListValue repeated_list_value = 317; + + // Test field-name-to-JSON-name convention. + // (protobuf says names can be any valid C/C++ identifier.) + int32 fieldname1 = 401; + int32 field_name2 = 402; + int32 _field_name3 = 403; + int32 field__name4_ = 404; + int32 field0name5 = 405; + int32 field_0_name6 = 406; + int32 fieldName7 = 407; + int32 FieldName8 = 408; + int32 field_Name9 = 409; + int32 Field_Name10 = 410; + int32 FIELD_NAME11 = 411; + int32 FIELD_name12 = 412; + int32 __field_name13 = 413; + int32 __Field_name14 = 414; + int32 field__name15 = 415; + int32 field__Name16 = 416; + int32 field_name17__ = 417; + int32 Field_name18__ = 418; + + // Reserved for testing unknown fields + reserved 501 to 510; +} + +message ForeignMessage { + int32 c = 1; +} + +enum ForeignEnum { + FOREIGN_FOO = 0; + FOREIGN_BAR = 1; + FOREIGN_BAZ = 2; +} + +message NullHypothesisProto3 {} + +message EnumOnlyProto3 { + enum Bool { + kFalse = 0; + kTrue = 1; + } +}