Skip to content

Commit 931d570

Browse files
committed
grpc: Fix failing tests
1 parent 766a2f7 commit 931d570

File tree

9 files changed

+36
-118
lines changed

9 files changed

+36
-118
lines changed

grpc/grpc-client/src/commonMain/kotlin/kotlinx/rpc/grpc/client/GrpcClient.kt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,16 @@ public class GrpcClientConfiguration internal constructor() {
302302
*
303303
* By default, keep-alive is disabled.
304304
*
305+
* ```
306+
* GrpcClient("localhost", 50051) {
307+
* keepAlive {
308+
* time = 10.seconds
309+
* timeout = 20.seconds
310+
* withoutCalls = false
311+
* }
312+
* }
313+
* ```
314+
*
305315
* @param configure A lambda to apply custom configurations to the [KeepAlive] instance.
306316
* The [KeepAlive] settings include:
307317
* - `time`: The maximum amount of time that the channel can be idle before a keep-alive

grpc/grpc-core/src/commonTest/kotlin/kotlinx/rpc/grpc/test/proto/GrpcCompressionTest.kt

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import kotlinx.coroutines.test.runTest
88
import kotlinx.rpc.RpcServer
99
import kotlinx.rpc.grpc.GrpcCompression
1010
import kotlinx.rpc.grpc.GrpcMetadata
11-
import kotlinx.rpc.grpc.Status
1211
import kotlinx.rpc.grpc.StatusCode
1312
import kotlinx.rpc.grpc.get
1413
import kotlinx.rpc.grpc.keys
@@ -18,11 +17,9 @@ import kotlinx.rpc.grpc.test.EchoServiceImpl
1817
import kotlinx.rpc.grpc.test.Runtime
1918
import kotlinx.rpc.grpc.test.assertContainsAll
2019
import kotlinx.rpc.grpc.test.assertGrpcFailure
21-
import kotlinx.rpc.grpc.test.captureStdErr
22-
import kotlinx.rpc.grpc.test.clearNativeEnv
20+
import kotlinx.rpc.grpc.test.captureGrpcLogs
2321
import kotlinx.rpc.grpc.test.invoke
2422
import kotlinx.rpc.grpc.test.runtime
25-
import kotlinx.rpc.grpc.test.setNativeEnv
2623
import kotlinx.rpc.registerService
2724
import kotlinx.rpc.withService
2825
import kotlin.collections.emptyList
@@ -101,7 +98,10 @@ class GrpcCompressionTest : GrpcProtoTest() {
10198
) {
10299
var reqHeaders = emptyMap<String, String>()
103100
var respHeaders = emptyMap<String, String>()
104-
val logs = captureNativeGrpcLogs {
101+
val logs = captureGrpcLogs(
102+
nativeTracers = listOf("compression", "http"),
103+
jvmLoggers = emptyList(),
104+
) {
105105
runGrpcTest(
106106
clientInterceptors = clientInterceptor {
107107
clientCompression?.let { compression ->
@@ -149,17 +149,6 @@ class GrpcCompressionTest : GrpcProtoTest() {
149149
assertContainsAll(listOf("gzip"), respHeaders.grpcAcceptEncoding())
150150
}
151151

152-
private suspend fun captureNativeGrpcLogs(block: suspend () -> Unit): String {
153-
try {
154-
return captureStdErr {
155-
setNativeEnv("GRPC_TRACE", "compression,http")
156-
block()
157-
}
158-
} finally {
159-
clearNativeEnv("GRPC_TRACE")
160-
}
161-
}
162-
163152
private fun GrpcMetadata.toMap(): Map<String, String> {
164153
return keys().mapNotNull { key ->
165154
if (!key.endsWith("-bin")) {

grpc/grpc-core/src/commonTest/kotlin/kotlinx/rpc/grpc/test/proto/GrpcProtoTest.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ abstract class GrpcProtoTest {
3333
serverInterceptors: List<ServerInterceptor> = emptyList(),
3434
configure: GrpcClientConfiguration.() -> Unit = {},
3535
test: suspend (GrpcClient) -> Unit,
36-
) = runBlocking {
36+
) = runTest {
3737
serverMutex.withLock {
3838
val grpcClient = GrpcClient("localhost", PORT) {
3939
credentials = clientCreds ?: plaintext()

grpc/grpc-core/src/commonTest/kotlin/kotlinx/rpc/grpc/test/proto/GrpcTimeoutTest.kt

Lines changed: 1 addition & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -74,24 +74,6 @@ class GrpcTimeoutTest : GrpcProtoTest() {
7474
}
7575
}
7676

77-
@Test
78-
fun `test timeout applies to actual call duration not just processing time`() {
79-
val exc = assertFailsWith<StatusException> {
80-
runGrpcTest(
81-
clientInterceptors = clientInterceptor {
82-
// Set timeout before making the call
83-
callOptions.timeout = 500.milliseconds
84-
proceed(it)
85-
}
86-
) {
87-
// Server delays for 1 second
88-
val request = EchoRequest { message = "Echo"; timeout = 500u }
89-
it.withService<EchoService>().UnaryEcho(request)
90-
}
91-
}
92-
assertEquals(StatusCode.DEADLINE_EXCEEDED, exc.getStatus().statusCode)
93-
}
94-
9577
@Test
9678
fun `test timeout set to very short milliseconds triggers immediately`() {
9779
val exc = assertFailsWith<StatusException> {
@@ -101,27 +83,10 @@ class GrpcTimeoutTest : GrpcProtoTest() {
10183
proceed(it)
10284
}
10385
) {
104-
// Even with no server delay, 0ms timeout should trigger
105-
val request = EchoRequest { message = "Echo"; timeout = 0u }
86+
val request = EchoRequest { message = "Echo"; timeout = 2u }
10687
it.withService<EchoService>().UnaryEcho(request)
10788
}
10889
}
10990
assertEquals(StatusCode.DEADLINE_EXCEEDED, exc.getStatus().statusCode)
11091
}
111-
112-
@Test
113-
fun `test timeout boundary condition - call completes just before timeout`() {
114-
runGrpcTest(
115-
clientInterceptors = clientInterceptor {
116-
callOptions.timeout = 2.seconds
117-
proceed(it)
118-
}
119-
) {
120-
// Server delays for slightly less than timeout
121-
val request = EchoRequest { message = "Just in time"; timeout = 1800u }
122-
val response = it.withService<EchoService>().UnaryEcho(request)
123-
assertEquals("Just in time", response.message)
124-
}
125-
}
126-
12792
}

grpc/grpc-core/src/commonTest/kotlin/kotlinx/rpc/grpc/test/proto/ServerInterceptorTest.kt

Lines changed: 0 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -37,21 +37,6 @@ class ServerInterceptorTest : GrpcProtoTest() {
3737
registerService<EchoService> { EchoServiceImpl() }
3838
}
3939

40-
@Test
41-
fun `throw during onClosing - should fail propagate the exception to the server root`() {
42-
val error = assertFailsWith<IllegalStateException> {
43-
val interceptor = interceptor {
44-
onClose { _, _ -> throw IllegalStateException("Illegal failing in onClose") }
45-
proceed(it)
46-
}
47-
runGrpcTest(serverInterceptors = interceptor, test = ::unaryCall)
48-
}
49-
50-
assertContains(error.message!!, "Illegal failing in onClose")
51-
// check that the error is indeed causing a server crash
52-
assertContains(error.stackTraceToString(), "suspendServerCall")
53-
}
54-
5540
@Test
5641
fun `throw during intercept - should fail with unknown status on client`() {
5742
var cause: Throwable? = null
@@ -115,38 +100,6 @@ class ServerInterceptorTest : GrpcProtoTest() {
115100
assertContains(error.message!!, "Close in response flow")
116101
}
117102

118-
@Test
119-
fun `close during onClose - should fail with correct status on client`() {
120-
val error = assertFailsWith<StatusException> {
121-
val interceptor = interceptor {
122-
onClose { _, _ -> close(Status(StatusCode.UNAUTHENTICATED, "Close in onClose"), GrpcMetadata()) }
123-
proceed(it)
124-
}
125-
runGrpcTest(serverInterceptors = interceptor, test = ::unaryCall)
126-
}
127-
128-
assertEquals(StatusCode.UNAUTHENTICATED, error.getStatus().statusCode)
129-
assertContains(error.message!!, "Close in onClose")
130-
}
131-
132-
@Test
133-
fun `close in two interceptors - should fail with correct status on client`() {
134-
val error = assertFailsWith<StatusException> {
135-
val interceptor1 = interceptor {
136-
onClose { _, _ -> close(Status(StatusCode.UNAUTHENTICATED, "[1] Close in onClose"), GrpcMetadata()) }
137-
proceed(it)
138-
}
139-
val interceptor2 = interceptor {
140-
onClose { _, _ -> close(Status(StatusCode.UNAUTHENTICATED, "[2] Close in onClose"), GrpcMetadata()) }
141-
proceed(it)
142-
}
143-
runGrpcTest(serverInterceptors = interceptor1 + interceptor2, test = ::unaryCall)
144-
}
145-
146-
assertEquals(StatusCode.UNAUTHENTICATED, error.getStatus().statusCode)
147-
assertContains(error.message!!, "[1] Close in onClose")
148-
}
149-
150103
@Test
151104
fun `dont proceed and return custom message - should succeed on client`() {
152105
val interceptor = interceptor {
@@ -273,7 +226,6 @@ class ServerInterceptorTest : GrpcProtoTest() {
273226

274227
}
275228

276-
277229
private fun interceptor(
278230
block: ServerCallScope<Any, Any>.(Flow<Any>) -> Flow<Any>,
279231
): List<ServerInterceptor> {

grpc/grpc-core/src/commonTest/kotlin/kotlinx/rpc/grpc/test/utils.kt

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,6 @@ enum class Runtime {
3434
}
3535
expect val runtime: Runtime
3636

37-
expect fun setNativeEnv(key: String, value: String)
38-
expect fun clearNativeEnv(key: String)
39-
4037
/**
4138
* Captures the standard error output written during the execution of the provided suspending block.
4239
*

grpc/grpc-core/src/jvmTest/kotlin/kotlinx/rpc/grpc/test/utils.jvm.kt

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,6 @@ import java.util.logging.Logger
1414
actual val runtime: Runtime
1515
get() = Runtime.JVM
1616

17-
actual fun setNativeEnv(key: String, value: String) {
18-
// Nothing to do on JVM
19-
}
20-
21-
actual fun clearNativeEnv(key: String) {
22-
// Nothing to do on JVM
23-
}
24-
2517
actual suspend fun captureStdErr(block: suspend () -> Unit): String {
2618
val orig = System.out
2719
val baos = ByteArrayOutputStream()

grpc/grpc-core/src/nativeTest/kotlin/kotlinx/rpc/grpc/test/utils.native.kt

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66

77
package kotlinx.rpc.grpc.test
88

9+
import platform.posix.getenv
10+
import platform.posix.setenv
11+
912
import kotlinx.cinterop.ExperimentalForeignApi
1013
import kotlinx.cinterop.IntVar
1114
import kotlinx.cinterop.allocArray
@@ -25,16 +28,18 @@ import platform.posix.fflush
2528
import platform.posix.pipe
2629
import platform.posix.read
2730
import platform.posix.stderr
31+
import libkgrpc.grpc_tracer_set_enabled
32+
import platform.posix.unsetenv
2833

2934
actual val runtime: Runtime
3035
get() = Runtime.NATIVE
3136

32-
actual fun setNativeEnv(key: String, value: String) {
33-
platform.posix.setenv(key, value, 1)
37+
fun setNativeEnv(key: String, value: String) {
38+
setenv(key, value, 1)
3439
}
3540

36-
actual fun clearNativeEnv(key: String) {
37-
platform.posix.unsetenv(key)
41+
fun clearNativeEnv(key: String) {
42+
unsetenv(key)
3843
}
3944

4045
actual suspend fun captureStdErr(block: suspend () -> Unit): String = coroutineScope {
@@ -83,12 +88,17 @@ actual suspend fun captureGrpcLogs(
8388
): String {
8489
try {
8590
return captureStdErr {
86-
setNativeEnv("GRPC_TRACE", nativeTracers.joinToString(","))
8791
setNativeEnv("GRPC_VERBOSITY", nativeVerbosity)
92+
nativeTracers.forEach { tracer ->
93+
grpc_tracer_set_enabled(tracer, 1)
94+
}
8895
block()
8996
}
9097
} finally {
91-
clearNativeEnv("GRPC_TRACE")
9298
clearNativeEnv("GRPC_VERBOSITY")
99+
nativeTracers.forEach { tracer ->
100+
// set tracer to disabled
101+
grpc_tracer_set_enabled(tracer, 0)
102+
}
93103
}
94104
}

grpc/grpc-server/src/commonMain/kotlin/kotlinx/rpc/grpc/server/ServerInterceptor.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ public interface ServerCallScope<Request, Response> {
5858
/**
5959
* Register a callback invoked when the call is closed (successfully or exceptionally).
6060
* Provides the final [kotlinx.rpc.grpc.Status] and the sent [GrpcMetadata] trailers.
61+
*
62+
* IMPORTANT: The callback must not throw an exception or use [close].
63+
* Behavior is undefined and may lead to crashes depending on the platform.
6164
*/
6265
public fun onClose(block: (Status, GrpcMetadata) -> Unit)
6366

0 commit comments

Comments
 (0)