Skip to content

Commit a6020d9

Browse files
Jozott00Mr3zee
authored andcommitted
grpc: Support enum and oneof field generation (#426)
* grpc-pb: Enum nullpointer exception state Signed-off-by: Johannes Zottele <[email protected]> * grpc-pb: Implement working enum constructor Signed-off-by: Johannes Zottele <[email protected]> * grpc-pb: Support OneOf Signed-off-by: Johannes Zottele <[email protected]> * grpc-pb: Fix imports Signed-off-by: Johannes Zottele <[email protected]> * grpc-pb: Fix lowercase name of oneof field Signed-off-by: Johannes Zottele <[email protected]> * grpc-pb: Address PR comments Signed-off-by: Johannes Zottele <[email protected]> --------- Signed-off-by: Johannes Zottele <[email protected]>
1 parent 160a666 commit a6020d9

File tree

10 files changed

+282
-51
lines changed

10 files changed

+282
-51
lines changed

grpc/grpc-core/build.gradle.kts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import kotlinx.rpc.proto.kotlinMultiplatform
77
import org.gradle.internal.extensions.stdlib.capitalized
88
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
99
import org.jetbrains.kotlin.gradle.tasks.CInteropProcess
10+
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
1011

1112
plugins {
1213
alias(libs.plugins.conventions.kmp)
@@ -159,5 +160,12 @@ rpc {
159160
dependsOn(gradle.includedBuild("protoc-gen").task(":jar"))
160161
}
161162
}
163+
164+
// generate protos before compiling tests
165+
project.tasks.withType<KotlinCompile>().configureEach {
166+
if (name.startsWith("compileTest")) {
167+
dependsOn(project.tasks.withType<BufGenerateTask>())
168+
}
169+
}
162170
}
163171
}

grpc/grpc-core/src/commonTest/kotlin/kotlinx/rpc/grpc/pb/ProtosTest.kt

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,20 @@
44

55
package kotlinx.rpc.grpc.pb
66

7+
import OneOfMsg
8+
import OneOfMsgInternal
9+
import invoke
710
import kotlinx.io.Buffer
811
import kotlinx.rpc.grpc.internal.MessageCodec
12+
import kotlinx.rpc.grpc.test.Enum
13+
import kotlinx.rpc.grpc.test.UsingEnum
14+
import kotlinx.rpc.grpc.test.UsingEnumInternal
915
import kotlinx.rpc.grpc.test.common.*
16+
import kotlinx.rpc.grpc.test.invoke
1017
import kotlin.test.Test
1118
import kotlin.test.assertEquals
1219
import kotlin.test.assertFailsWith
20+
import kotlin.test.assertNull
1321

1422
class ProtosTest {
1523

@@ -84,5 +92,78 @@ class ProtosTest {
8492
}
8593
}
8694

95+
@Test
96+
fun testEnumUnrecognized() {
97+
// write unknown enum value
98+
val buffer = Buffer()
99+
val encoder = WireEncoder(buffer)
100+
encoder.writeEnum(1, 50)
101+
encoder.flush()
102+
103+
val decodedMsg = UsingEnumInternal.CODEC.decode(buffer)
104+
assertEquals(Enum.UNRECOGNIZED(50), decodedMsg.enum)
105+
}
106+
107+
@Test
108+
fun testEnumAlias() {
109+
val msg = UsingEnum {
110+
enum = Enum.ONE_SECOND
111+
}
112+
113+
val decodedMsg = decodeEncode(msg, UsingEnumInternal.CODEC)
114+
assertEquals(Enum.ONE, decodedMsg.enum)
115+
assertEquals(Enum.ONE_SECOND, decodedMsg.enum)
116+
}
117+
118+
@Test
119+
fun testDefault() {
120+
// create message without enum field set
121+
val msg = UsingEnum {}
122+
123+
val buffer = UsingEnumInternal.CODEC.encode(msg) as Buffer
124+
// buffer should be empty (default is not in wire)
125+
assertEquals(0, buffer.size)
126+
127+
val decoded = UsingEnumInternal.CODEC.decode(buffer)
128+
assertEquals(Enum.ZERO, decoded.enum)
129+
}
130+
131+
@Test
132+
fun testOneOf() {
133+
val msg1 = OneOfMsg {
134+
field = OneOfMsg.Field.Sint(23)
135+
}
136+
val decoded1 = decodeEncode(msg1, OneOfMsgInternal.CODEC)
137+
assertEquals(OneOfMsg.Field.Sint(23), decoded1.field)
138+
139+
val msg2 = OneOfMsg {
140+
field = OneOfMsg.Field.Fixed(21u)
141+
}
142+
val decoded2 = decodeEncode(msg2, OneOfMsgInternal.CODEC)
143+
assertEquals(OneOfMsg.Field.Fixed(21u), decoded2.field)
144+
}
145+
146+
@Test
147+
fun testOneOfLastWins() {
148+
// write two values on the oneOf field.
149+
// the second value must be the one stored during decoding.
150+
val buffer = Buffer()
151+
val encoder = WireEncoder(buffer)
152+
encoder.writeInt32(2, 99)
153+
encoder.writeFixed64(3, 123u)
154+
encoder.flush()
155+
156+
val decoded = OneOfMsgInternal.CODEC.decode(buffer)
157+
assertEquals(OneOfMsg.Field.Fixed(123u), decoded.field)
158+
}
159+
160+
@Test
161+
fun testOneOfNull() {
162+
// write two values on the oneOf field.
163+
// the second value must be the one stored during decoding.
164+
val buffer = Buffer()
165+
val decoded = OneOfMsgInternal.CODEC.decode(buffer)
166+
assertNull(decoded.field)
167+
}
87168

88169
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
message OneOfMsg {
2+
oneof field {
3+
int32 sint = 2;
4+
fixed64 fixed = 3;
5+
}
6+
}

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,10 +102,12 @@ open class CodeGenerator(
102102

103103
internal fun whenBlock(
104104
condition: String? = null,
105+
prefix: String = "",
105106
block: (CodeGenerator.() -> Unit),
106107
) {
108+
val pre = if (prefix.isNotEmpty()) prefix.trim() + " " else ""
107109
val cond = condition?.let { " ($it)" } ?: ""
108-
scope("when$cond", block = block)
110+
scope("${pre}when$cond", block = block)
109111
}
110112

111113
internal fun whenCase(

0 commit comments

Comments
 (0)