Skip to content

Commit 78da810

Browse files
authored
protobuf: Enable all conformance tests (#473)
1 parent 000b2fa commit 78da810

File tree

14 files changed

+20601
-29
lines changed

14 files changed

+20601
-29
lines changed

.github/workflows/protobuf-conformance.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,10 @@ jobs:
1818
- name: Setup Gradle
1919
uses: gradle/actions/setup-gradle@v4
2020
- name: Run Protobuf Conformance Test Generation
21-
run: ./gradlew tests:protobuf-conformance:bufGenerateTest --info --stacktrace
21+
run: ./gradlew tests:protobuf-conformance:bufGenerateMain --info --stacktrace
2222
- name: Check if protobuf-conformance test is up-to-date
2323
run: |
2424
if [[ -n "$(git status --porcelain | grep tests/protobuf-conformance/)" ]]; then
25-
echo "Protobuf conformance test is not up to date. Please run './gradlew tests:protobuf-conformance:bufGenerateTest' and commit changes"
25+
echo "Protobuf conformance test is not up to date. Please run './gradlew tests:protobuf-conformance:bufGenerateMain' and commit changes"
2626
exit 1
2727
fi

protobuf/protobuf-core/src/commonMain/kotlin/kotlinx/rpc/protobuf/internal/ProtobufException.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
package kotlinx.rpc.protobuf.internal
66

7+
import kotlinx.rpc.internal.utils.InternalRpcApi
8+
79
public sealed class ProtobufException : RuntimeException {
810
protected constructor(message: String, cause: Throwable? = null) : super(message, cause)
911
}
@@ -13,6 +15,7 @@ public class ProtobufDecodingException : ProtobufException {
1315
public constructor(message: String, cause: Throwable? = null) : super(message, cause)
1416

1517
public companion object Companion {
18+
@InternalRpcApi
1619
public fun missingRequiredField(messageName: String, fieldName: String): ProtobufDecodingException =
1720
ProtobufDecodingException("Message '$messageName' is missing a required field: $fieldName")
1821

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1-
syntax = "proto3";
2-
31
package kotlinx.rpc.protobuf.test;
42

53
import "presence_check.proto";
64

75
message TestMap {
86
map<string, int64> primitives = 1;
97
map<int32, kotlinx.rpc.protobuf.test.PresenceCheck> messages = 2;
8+
map<int32, int32> map_int32_int32 = 3;
109
}

protoc-gen/common/src/main/kotlin/kotlinx/rpc/protoc/gen/core/model/model.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,11 +91,14 @@ data class FieldDeclaration(
9191

9292
val isPartOfOneof: Boolean = dec.realContainingOneof != null
9393

94+
val isPartOfMapEntry = dec.containingType.options.mapEntry
95+
9496
// aligns with edition settings and backward compatibility with proto2 and proto3
9597
val nullable: Boolean = (dec.hasPresence() && !dec.isRequired && !dec.hasDefaultValue()
9698
&& !dec.isRepeated // repeated fields cannot be nullable (just empty)
9799
&& !isPartOfOneof // upper conditions would match oneof inner fields
98100
&& type !is FieldType.Message // messages must not be null (to conform protobuf standards)
101+
&& !isPartOfMapEntry // map entry fields cannot be null
99102
)
100103
|| type is FieldType.OneOf // all OneOf fields are nullable
101104
val number: Int = dec.number

protoc-gen/protobuf/src/main/kotlin/kotlinx/rpc/protoc/gen/ModelToProtobufKotlinCommonGenerator.kt

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -911,25 +911,49 @@ class ModelToProtobufKotlinCommonGenerator(
911911
return type.defaultValue ?: error("No default value for field $name")
912912
}
913913

914-
return when (val value = dec.defaultValue) {
915-
is String -> {
914+
val value = dec.defaultValue
915+
return when {
916+
value is String -> {
916917
"\"$value\""
917918
}
918919

919-
is ByteString -> {
920+
value is ByteString -> {
920921
"BytesDefaults.$name"
921922
}
922923

923-
is Descriptors.EnumValueDescriptor -> {
924+
value is Descriptors.EnumValueDescriptor -> {
924925
value.fqName().safeFullName()
925926
}
926927

928+
value is Int && (type == FieldType.IntegralType.UINT32 || type == FieldType.IntegralType.FIXED32) -> {
929+
Integer.toUnsignedString(value) + "u"
930+
}
931+
932+
value is Long && (type == FieldType.IntegralType.UINT64 || type == FieldType.IntegralType.FIXED64) -> {
933+
java.lang.Long.toUnsignedString(value) + "uL"
934+
}
935+
936+
value is Float -> when {
937+
value.isNaN() -> "Float.NaN"
938+
value == Float.POSITIVE_INFINITY -> "Float.POSITIVE_INFINITY"
939+
value == Float.NEGATIVE_INFINITY -> "Float.NEGATIVE_INFINITY"
940+
else -> "Float.fromBits(0x%08X)".format(java.lang.Float.floatToRawIntBits(value))
941+
}
942+
943+
value is Double -> when {
944+
value.isNaN() -> "Double.NaN"
945+
value == Double.POSITIVE_INFINITY -> "Double.POSITIVE_INFINITY"
946+
value == Double.NEGATIVE_INFINITY -> "Double.NEGATIVE_INFINITY"
947+
else -> "Double.fromBits(0x%016XL)".format(java.lang.Double.doubleToRawLongBits(value))
948+
}
949+
927950
else -> {
928951
"${value}${type.scalarDefaultSuffix()}"
929952
}
930953
}
931954
}
932955

956+
933957
private fun FieldType.decodeEncodeFuncName(): String? = when (this) {
934958
FieldType.IntegralType.STRING -> "String"
935959
FieldType.IntegralType.BYTES -> "Bytes"

tests/protobuf-conformance/build.gradle.kts

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -48,16 +48,6 @@ tasks.withType<BufGenerateTask>().configureEach {
4848
}
4949
}
5050

51-
protoSourceSets {
52-
main {
53-
proto {
54-
exclude("**/test_messages_proto2.proto")
55-
exclude("**/test_messages_proto2_editions.proto")
56-
exclude("**/test_messages_edition2023.proto")
57-
}
58-
}
59-
}
60-
6151
val mockClientJar = tasks.register<Jar>("mockClientJar") {
6252
archiveBaseName.set("mockClient")
6353
archiveVersion.set("")
@@ -91,7 +81,8 @@ val generateConformanceTests = tasks.register<JavaExec>("generateConformanceTest
9181
}
9282

9383
val conformanceTest = properties.getOrDefault("conformance.test", "").toString()
94-
val conformanceTestDebug = properties.getOrDefault("conformance.test.debug", "false").toString().toBooleanStrictOrNull() ?: false
84+
val conformanceTestDebug =
85+
properties.getOrDefault("conformance.test.debug", "false").toString().toBooleanStrictOrNull() ?: false
9586

9687
val generateConformanceFileDescriptorSet = tasks
9788
.withType<GenerateConformanceFileDescriptorSet>()
Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
@file:OptIn(ExperimentalRpcApi::class, InternalRpcApi::class)
2+
package com.google.protobuf_test_messages.edition2023
3+
4+
import kotlin.jvm.JvmInline
5+
import kotlinx.rpc.internal.utils.*
6+
7+
@kotlinx.rpc.grpc.codec.WithCodec(com.google.protobuf_test_messages.edition2023.ComplexMessageInternal.CODEC::class)
8+
interface ComplexMessage {
9+
val d: Int?
10+
11+
companion object
12+
}
13+
14+
@kotlinx.rpc.grpc.codec.WithCodec(com.google.protobuf_test_messages.edition2023.TestAllTypesEdition2023Internal.CODEC::class)
15+
interface TestAllTypesEdition2023 {
16+
val optionalInt32: Int?
17+
val optionalInt64: Long?
18+
val optionalUint32: UInt?
19+
val optionalUint64: ULong?
20+
val optionalSint32: Int?
21+
val optionalSint64: Long?
22+
val optionalFixed32: UInt?
23+
val optionalFixed64: ULong?
24+
val optionalSfixed32: Int?
25+
val optionalSfixed64: Long?
26+
val optionalFloat: Float?
27+
val optionalDouble: Double?
28+
val optionalBool: Boolean?
29+
val optionalString: String?
30+
val optionalBytes: ByteArray?
31+
val optionalNestedMessage: com.google.protobuf_test_messages.edition2023.TestAllTypesEdition2023.NestedMessage
32+
val optionalForeignMessage: com.google.protobuf_test_messages.edition2023.ForeignMessageEdition2023
33+
val optionalNestedEnum: com.google.protobuf_test_messages.edition2023.TestAllTypesEdition2023.NestedEnum?
34+
val optionalForeignEnum: com.google.protobuf_test_messages.edition2023.ForeignEnumEdition2023?
35+
val optionalStringPiece: String?
36+
val optionalCord: String?
37+
val recursiveMessage: com.google.protobuf_test_messages.edition2023.TestAllTypesEdition2023
38+
val repeatedInt32: List<kotlin.Int>
39+
val repeatedInt64: List<kotlin.Long>
40+
val repeatedUint32: List<kotlin.UInt>
41+
val repeatedUint64: List<kotlin.ULong>
42+
val repeatedSint32: List<kotlin.Int>
43+
val repeatedSint64: List<kotlin.Long>
44+
val repeatedFixed32: List<kotlin.UInt>
45+
val repeatedFixed64: List<kotlin.ULong>
46+
val repeatedSfixed32: List<kotlin.Int>
47+
val repeatedSfixed64: List<kotlin.Long>
48+
val repeatedFloat: List<kotlin.Float>
49+
val repeatedDouble: List<kotlin.Double>
50+
val repeatedBool: List<kotlin.Boolean>
51+
val repeatedString: List<kotlin.String>
52+
val repeatedBytes: List<kotlin.ByteArray>
53+
val repeatedNestedMessage: List<com.google.protobuf_test_messages.edition2023.TestAllTypesEdition2023.NestedMessage>
54+
val repeatedForeignMessage: List<com.google.protobuf_test_messages.edition2023.ForeignMessageEdition2023>
55+
val repeatedNestedEnum: List<com.google.protobuf_test_messages.edition2023.TestAllTypesEdition2023.NestedEnum>
56+
val repeatedForeignEnum: List<com.google.protobuf_test_messages.edition2023.ForeignEnumEdition2023>
57+
val repeatedStringPiece: List<kotlin.String>
58+
val repeatedCord: List<kotlin.String>
59+
val packedInt32: List<kotlin.Int>
60+
val packedInt64: List<kotlin.Long>
61+
val packedUint32: List<kotlin.UInt>
62+
val packedUint64: List<kotlin.ULong>
63+
val packedSint32: List<kotlin.Int>
64+
val packedSint64: List<kotlin.Long>
65+
val packedFixed32: List<kotlin.UInt>
66+
val packedFixed64: List<kotlin.ULong>
67+
val packedSfixed32: List<kotlin.Int>
68+
val packedSfixed64: List<kotlin.Long>
69+
val packedFloat: List<kotlin.Float>
70+
val packedDouble: List<kotlin.Double>
71+
val packedBool: List<kotlin.Boolean>
72+
val packedNestedEnum: List<com.google.protobuf_test_messages.edition2023.TestAllTypesEdition2023.NestedEnum>
73+
val unpackedInt32: List<kotlin.Int>
74+
val unpackedInt64: List<kotlin.Long>
75+
val unpackedUint32: List<kotlin.UInt>
76+
val unpackedUint64: List<kotlin.ULong>
77+
val unpackedSint32: List<kotlin.Int>
78+
val unpackedSint64: List<kotlin.Long>
79+
val unpackedFixed32: List<kotlin.UInt>
80+
val unpackedFixed64: List<kotlin.ULong>
81+
val unpackedSfixed32: List<kotlin.Int>
82+
val unpackedSfixed64: List<kotlin.Long>
83+
val unpackedFloat: List<kotlin.Float>
84+
val unpackedDouble: List<kotlin.Double>
85+
val unpackedBool: List<kotlin.Boolean>
86+
val unpackedNestedEnum: List<com.google.protobuf_test_messages.edition2023.TestAllTypesEdition2023.NestedEnum>
87+
val mapInt32Int32: Map<kotlin.Int, kotlin.Int>
88+
val mapInt64Int64: Map<kotlin.Long, kotlin.Long>
89+
val mapUint32Uint32: Map<kotlin.UInt, kotlin.UInt>
90+
val mapUint64Uint64: Map<kotlin.ULong, kotlin.ULong>
91+
val mapSint32Sint32: Map<kotlin.Int, kotlin.Int>
92+
val mapSint64Sint64: Map<kotlin.Long, kotlin.Long>
93+
val mapFixed32Fixed32: Map<kotlin.UInt, kotlin.UInt>
94+
val mapFixed64Fixed64: Map<kotlin.ULong, kotlin.ULong>
95+
val mapSfixed32Sfixed32: Map<kotlin.Int, kotlin.Int>
96+
val mapSfixed64Sfixed64: Map<kotlin.Long, kotlin.Long>
97+
val mapInt32Float: Map<kotlin.Int, kotlin.Float>
98+
val mapInt32Double: Map<kotlin.Int, kotlin.Double>
99+
val mapBoolBool: Map<kotlin.Boolean, kotlin.Boolean>
100+
val mapStringString: Map<kotlin.String, kotlin.String>
101+
val mapStringBytes: Map<kotlin.String, kotlin.ByteArray>
102+
val mapStringNestedMessage: Map<kotlin.String, com.google.protobuf_test_messages.edition2023.TestAllTypesEdition2023.NestedMessage>
103+
val mapStringForeignMessage: Map<kotlin.String, com.google.protobuf_test_messages.edition2023.ForeignMessageEdition2023>
104+
val mapStringNestedEnum: Map<kotlin.String, com.google.protobuf_test_messages.edition2023.TestAllTypesEdition2023.NestedEnum>
105+
val mapStringForeignEnum: Map<kotlin.String, com.google.protobuf_test_messages.edition2023.ForeignEnumEdition2023>
106+
val groupliketype: com.google.protobuf_test_messages.edition2023.TestAllTypesEdition2023.GroupLikeType
107+
val delimitedField: com.google.protobuf_test_messages.edition2023.TestAllTypesEdition2023.GroupLikeType
108+
val oneofField: com.google.protobuf_test_messages.edition2023.TestAllTypesEdition2023.OneofField?
109+
110+
sealed interface OneofField {
111+
@JvmInline
112+
value class OneofUint32(val value: UInt): OneofField
113+
114+
@JvmInline
115+
value class OneofNestedMessage(
116+
val value: com.google.protobuf_test_messages.edition2023.TestAllTypesEdition2023.NestedMessage,
117+
): OneofField
118+
119+
@JvmInline
120+
value class OneofString(val value: String): OneofField
121+
122+
@JvmInline
123+
value class OneofBytes(val value: ByteArray): OneofField
124+
125+
@JvmInline
126+
value class OneofBool(val value: Boolean): OneofField
127+
128+
@JvmInline
129+
value class OneofUint64(val value: ULong): OneofField
130+
131+
@JvmInline
132+
value class OneofFloat(val value: Float): OneofField
133+
134+
@JvmInline
135+
value class OneofDouble(val value: Double): OneofField
136+
137+
@JvmInline
138+
value class OneofEnum(
139+
val value: com.google.protobuf_test_messages.edition2023.TestAllTypesEdition2023.NestedEnum,
140+
): OneofField
141+
}
142+
143+
@kotlinx.rpc.grpc.codec.WithCodec(com.google.protobuf_test_messages.edition2023.TestAllTypesEdition2023Internal.NestedMessageInternal.CODEC::class)
144+
interface NestedMessage {
145+
val a: Int?
146+
val corecursive: com.google.protobuf_test_messages.edition2023.TestAllTypesEdition2023
147+
148+
companion object
149+
}
150+
151+
interface GroupLikeType {
152+
val groupInt32: Int?
153+
val groupUint32: UInt?
154+
155+
companion object
156+
}
157+
158+
sealed class NestedEnum(open val number: Int) {
159+
object FOO: NestedEnum(number = 0)
160+
161+
object BAR: NestedEnum(number = 1)
162+
163+
object BAZ: NestedEnum(number = 2)
164+
165+
object NEG: NestedEnum(number = -1)
166+
167+
data class UNRECOGNIZED(override val number: Int): NestedEnum(number)
168+
169+
companion object {
170+
val entries: List<NestedEnum> by lazy { listOf(NEG, FOO, BAR, BAZ) }
171+
}
172+
}
173+
174+
companion object
175+
}
176+
177+
@kotlinx.rpc.grpc.codec.WithCodec(com.google.protobuf_test_messages.edition2023.ForeignMessageEdition2023Internal.CODEC::class)
178+
interface ForeignMessageEdition2023 {
179+
val c: Int?
180+
181+
companion object
182+
}
183+
184+
@kotlinx.rpc.grpc.codec.WithCodec(com.google.protobuf_test_messages.edition2023.GroupLikeTypeInternal.CODEC::class)
185+
interface GroupLikeType {
186+
val c: Int?
187+
188+
companion object
189+
}
190+
191+
sealed class ForeignEnumEdition2023(open val number: Int) {
192+
object FOREIGN_FOO: ForeignEnumEdition2023(number = 0)
193+
194+
object FOREIGN_BAR: ForeignEnumEdition2023(number = 1)
195+
196+
object FOREIGN_BAZ: ForeignEnumEdition2023(number = 2)
197+
198+
data class UNRECOGNIZED(override val number: Int): ForeignEnumEdition2023(number)
199+
200+
companion object {
201+
val entries: List<ForeignEnumEdition2023> by lazy { listOf(FOREIGN_FOO, FOREIGN_BAR, FOREIGN_BAZ) }
202+
}
203+
}
204+

0 commit comments

Comments
 (0)