Skip to content

Commit 7e3ae1f

Browse files
committed
refactor exception and status assertions
1 parent 5e35e7c commit 7e3ae1f

File tree

10 files changed

+124
-96
lines changed

10 files changed

+124
-96
lines changed

kroto-plus-coroutines/src/main/kotlin/com/github/marcoferrer/krotoplus/coroutines/client/ClientCalls.kt

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ import kotlinx.coroutines.CancellableContinuation
3434
import kotlinx.coroutines.CompletableDeferred
3535
import kotlinx.coroutines.CoroutineScope
3636
import kotlinx.coroutines.Deferred
37-
import kotlinx.coroutines.Dispatchers
3837
import kotlinx.coroutines.Job
3938
import kotlinx.coroutines.channels.Channel
4039
import kotlinx.coroutines.channels.ReceiveChannel
@@ -210,12 +209,9 @@ public fun <ReqT, RespT> clientCallClientStreaming(
210209

211210
val rpcScope = newRpcScope(callOptions, method)
212211
val response = CompletableDeferred<RespT>(parent = rpcScope.coroutineContext[Job])
213-
val requestChannel = rpcScope.actor<ReqT>(
214-
capacity = Channel.RENDEZVOUS,
215-
context = Dispatchers.Unconfined
216-
) {
212+
val requestChannel = rpcScope.actor<ReqT>(capacity = Channel.RENDEZVOUS) {
217213
val responseObserver = ClientStreamingResponseObserver(
218-
this, this@actor.channel, response
214+
this@actor.channel, response
219215
)
220216

221217
val call = channel

kroto-plus-coroutines/src/main/kotlin/com/github/marcoferrer/krotoplus/coroutines/client/ClientResponseObservers.kt

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,6 @@ internal class ServerStreamingResponseObserver<ReqT, RespT>: StatefulClientRespo
9393
}
9494

9595
internal class ClientStreamingResponseObserver<ReqT, RespT>(
96-
private val rpcScope: CoroutineScope,
9796
private val requestChannel: Channel<ReqT>,
9897
private val response: CompletableDeferred<RespT>
9998
) : StatefulClientResponseObserver<ReqT, RespT>() {
@@ -128,8 +127,13 @@ internal class ClientStreamingResponseObserver<ReqT, RespT>(
128127

129128
override fun onError(t: Throwable) {
130129
isAborted.set(true)
131-
response.completeExceptionally(t)
132-
requestChannel.close(t)
130+
if(t is CancellationException){
131+
requestChannel.cancel(t)
132+
response.cancel(t)
133+
}else{
134+
requestChannel.close(t)
135+
response.completeExceptionally(t)
136+
}
133137
readyObserver.cancel(t)
134138
}
135139

@@ -180,7 +184,7 @@ internal class BidiStreamingResponseObserver<ReqT, RespT>(
180184
}
181185
}
182186

183-
responseChannel = flow<RespT> {
187+
responseChannel = flow {
184188
var error: Throwable? = null
185189
try {
186190
inboundChannel.consumeEach { message ->
@@ -232,8 +236,12 @@ internal class BidiStreamingResponseObserver<ReqT, RespT>(
232236

233237
override fun onError(t: Throwable) {
234238
isAborted.set(true)
235-
inboundChannel.close(t)
236239
requestChannel.close(t)
240+
if(t is CancellationException){
241+
inboundChannel.cancel(t)
242+
}else{
243+
inboundChannel.close(t)
244+
}
237245
readyObserver.cancel(t)
238246
}
239247

kroto-plus-coroutines/src/test/kotlin/com/github/marcoferrer/krotoplus/coroutines/RpcCallTest.kt

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,50 @@ abstract class RpcCallTest<ReqT, RespT>(
6161
fun setupCall() {
6262
callState = RpcStateInterceptor()
6363
CALL_TRACE_ENABLED = true
64+
// mockkObject(Testing)
65+
//
66+
// every { Testing.asyncClientStreamingCallK<ReqT, RespT>(any(), any()) } answers answer@ {
67+
// val call = firstArg<ClientCall<ReqT, RespT>>()
68+
// val responseObserver = secondArg<ClientResponseObserver<ReqT, RespT>>()
69+
//
70+
// val reqObserver = ClientCalls.asyncClientStreamingCall(call, object: ClientResponseObserver<ReqT, RespT>{
71+
// override fun onNext(value: RespT) {
72+
// responseObserver.onNext(value)
73+
// }
74+
//
75+
// override fun onError(t: Throwable) {
76+
// println("Client: Response observer onError(${t.toDebugString()})")
77+
// responseObserver.onError(t)
78+
// }
79+
//
80+
// override fun onCompleted() {
81+
// println("Client: Response observer onComplete()")
82+
// responseObserver.onCompleted()
83+
// }
84+
//
85+
// override fun beforeStart(requestStream: ClientCallStreamObserver<ReqT>) {
86+
// responseObserver.beforeStart(requestStream)
87+
// }
88+
//
89+
// } as StreamObserver<RespT>)
90+
//
91+
// return@answer object : StreamObserver<ReqT> {
92+
// override fun onNext(value: ReqT) {
93+
// reqObserver.onNext(value)
94+
// }
95+
//
96+
// override fun onError(t: Throwable) {
97+
// println("Client: Request observer onError(${t.toDebugString()})")
98+
// reqObserver.onError(t)
99+
// }
100+
//
101+
// override fun onCompleted() {
102+
// println("Client: Request observer onComplete()")
103+
// reqObserver.onCompleted()
104+
// }
105+
//
106+
// }
107+
// }
64108
}
65109

66110
fun registerService(service: BindableService){

kroto-plus-coroutines/src/test/kotlin/com/github/marcoferrer/krotoplus/coroutines/client/ClientCallBidiStreamingTests.kt

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ package com.github.marcoferrer.krotoplus.coroutines.client
2020
import com.github.marcoferrer.krotoplus.coroutines.RpcCallTest
2121
import com.github.marcoferrer.krotoplus.coroutines.utils.assertFails
2222
import com.github.marcoferrer.krotoplus.coroutines.utils.assertFailsWithCancellation
23-
import com.github.marcoferrer.krotoplus.coroutines.utils.assertFailsWithStatus2
23+
import com.github.marcoferrer.krotoplus.coroutines.utils.assertFailsWithStatus
2424
import com.github.marcoferrer.krotoplus.coroutines.utils.matchStatus
2525
import com.github.marcoferrer.krotoplus.coroutines.utils.matchThrowable
2626
import com.github.marcoferrer.krotoplus.coroutines.withCoroutineContext
@@ -163,7 +163,7 @@ class ClientCallBidiStreamingTests :
163163
.setName(it.toString())
164164
.build())
165165
}
166-
assertFailsWithStatus2(Status.INVALID_ARGUMENT) {
166+
assertFailsWithStatus(Status.INVALID_ARGUMENT) {
167167
send(HelloRequest.newBuilder()
168168
.setName("fails")
169169
.build()
@@ -175,7 +175,7 @@ class ClientCallBidiStreamingTests :
175175
repeat(3) {
176176
assertEquals("Req:#$it/Resp:#$it", responseChannel.receive().message)
177177
}
178-
assertFailsWithStatus2(Status.INVALID_ARGUMENT) {
178+
assertFailsWithStatus(Status.INVALID_ARGUMENT) {
179179
responseChannel.receive().message
180180
}
181181
}
@@ -303,7 +303,7 @@ class ClientCallBidiStreamingTests :
303303

304304
launch(Dispatchers.Default) {
305305
launch(start = CoroutineStart.UNDISPATCHED) {
306-
assertFailsWithStatus2(Status.CANCELLED) {
306+
assertFailsWithStatus(Status.CANCELLED) {
307307
repeat(3) {
308308
requestChannel.send(
309309
HelloRequest.newBuilder()
@@ -317,7 +317,7 @@ class ClientCallBidiStreamingTests :
317317
launch {
318318
error("cancel")
319319
}
320-
assertFailsWithStatus2(Status.CANCELLED) {
320+
assertFailsWithStatus(Status.CANCELLED) {
321321
callChannel.responseChannel.receive().message
322322
}
323323
}
@@ -354,7 +354,7 @@ class ClientCallBidiStreamingTests :
354354
requestChannel.close(expectedException)
355355
}
356356

357-
assertFailsWithStatus2(Status.CANCELLED, "CANCELLED: $expectedCancelMessage") {
357+
assertFailsWithStatus(Status.CANCELLED, "CANCELLED: $expectedCancelMessage") {
358358
responseChannel.consumeAsFlow()
359359
.map { it.message }
360360
.collect { result.add(it) }
@@ -392,12 +392,13 @@ class ClientCallBidiStreamingTests :
392392
result.add(responseChannel.receive().message)
393393
requestChannel.close(expectedException)
394394

395-
assertFailsWithStatus2(Status.CANCELLED, "CANCELLED: $expectedCancelMessage") {
395+
assertFailsWithStatus(Status.CANCELLED, "CANCELLED: $expectedCancelMessage") {
396396
responseChannel.consumeAsFlow()
397397
.collect { result.add(it.message) }
398398
}
399399
}
400400

401+
callState.client.cancelled.assertBlocking{ "Client must be cancelled" }
401402

402403
assertEquals(1, result.size)
403404
result.forEachIndexed { index, message ->
@@ -419,7 +420,7 @@ class ClientCallBidiStreamingTests :
419420

420421
runTest {
421422
launch {
422-
assertFailsWithStatus2(Status.CANCELLED) {
423+
assertFailsWithStatus(Status.CANCELLED) {
423424
repeat(10) {
424425
requestChannel.send(
425426
HelloRequest.newBuilder()

kroto-plus-coroutines/src/test/kotlin/com/github/marcoferrer/krotoplus/coroutines/client/ClientCallClientStreamingTests.kt

Lines changed: 42 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,8 @@ package com.github.marcoferrer.krotoplus.coroutines.client
1919

2020
import com.github.marcoferrer.krotoplus.coroutines.RpcCallTest
2121
import com.github.marcoferrer.krotoplus.coroutines.utils.assertExEquals
22-
import com.github.marcoferrer.krotoplus.coroutines.utils.assertFails
22+
import com.github.marcoferrer.krotoplus.coroutines.utils.assertFailsWithCancellation
2323
import com.github.marcoferrer.krotoplus.coroutines.utils.assertFailsWithStatus
24-
import com.github.marcoferrer.krotoplus.coroutines.utils.assertFailsWithStatus2
2524
import com.github.marcoferrer.krotoplus.coroutines.utils.matchThrowable
2625
import com.github.marcoferrer.krotoplus.coroutines.utils.toDebugString
2726
import com.github.marcoferrer.krotoplus.coroutines.withCoroutineContext
@@ -31,10 +30,11 @@ import io.grpc.examples.helloworld.GreeterGrpc
3130
import io.grpc.examples.helloworld.HelloReply
3231
import io.grpc.examples.helloworld.HelloRequest
3332
import io.grpc.stub.StreamObserver
33+
import io.mockk.coVerify
3434
import io.mockk.spyk
3535
import io.mockk.verify
36-
import kotlinx.coroutines.CancellationException
3736
import kotlinx.coroutines.CoroutineStart
37+
import kotlinx.coroutines.Deferred
3838
import kotlinx.coroutines.Dispatchers
3939
import kotlinx.coroutines.Job
4040
import kotlinx.coroutines.cancel
@@ -176,24 +176,28 @@ class ClientCallClientStreamingTests :
176176
requestsSent++
177177
requestChannel.send(
178178
HelloRequest.newBuilder()
179-
.setName(it.toString())
179+
.setName(0.toString())
180180
.build()
181181
)
182182
}
183-
assertFailsWithStatus(Status.INVALID_ARGUMENT) {
184-
requestChannel.send(
185-
HelloRequest.newBuilder()
186-
.setName("request")
187-
.build()
188-
)
189-
}
190-
}
191-
assertFailsWithStatus(Status.INVALID_ARGUMENT) {
192-
response.await().message
193183
}
194184
}
195185
}
196186

187+
assertFailsWithStatus(Status.INVALID_ARGUMENT) {
188+
runBlocking { response.await().message }
189+
}
190+
assertFailsWithStatus(Status.INVALID_ARGUMENT) {
191+
runBlocking {
192+
requestChannel.send(
193+
HelloRequest.newBuilder()
194+
.setName("request")
195+
.build()
196+
)
197+
}
198+
}
199+
200+
197201
assertEquals(2,requestsSent)
198202
assert(requestChannel.isClosedForSend) { "Request channel should be closed for send" }
199203
}
@@ -213,11 +217,11 @@ class ClientCallClientStreamingTests :
213217
launch(Dispatchers.Default) {
214218
val job = launch {
215219
launch(start = CoroutineStart.UNDISPATCHED){
216-
assertFailsWithStatus(Status.CANCELLED) {
220+
assertFailsWithCancellation {
217221
response.await().message
218222
}
219223
}
220-
assertFailsWithStatus(Status.CANCELLED) {
224+
assertFailsWithCancellation {
221225
repeat(3) {
222226
delay(5)
223227
requestChannel.send(
@@ -236,6 +240,8 @@ class ClientCallClientStreamingTests :
236240
}
237241
}
238242

243+
callState.client.cancelled.assertBlocking { "Client must be cancelled" }
244+
239245
verify(exactly = 1) { rpcSpy.call.cancel(any(), any()) }
240246
assert(requestChannel.isClosedForSend) { "Request channel should be closed for send" }
241247
}
@@ -247,37 +253,42 @@ class ClientCallClientStreamingTests :
247253
setupServerHandlerNoop()
248254

249255
lateinit var requestChannel: SendChannel<HelloRequest>
250-
assertFails<CancellationException> {
256+
lateinit var response: Deferred<HelloReply>
257+
258+
assertFailsWithCancellation {
251259
runBlocking {
252260
launch(start = CoroutineStart.UNDISPATCHED) {
253261
val callChannel = stub
254262
.withCoroutineContext()
255263
.clientCallClientStreaming(methodDescriptor)
256264

257-
requestChannel = callChannel.requestChannel
265+
requestChannel = spyk(callChannel.requestChannel)
266+
response = callChannel.response
258267

259-
val job = launch {
260-
callChannel.response.await().message
261-
}
262-
assertFailsWithStatus(Status.CANCELLED) {
268+
assertFailsWithCancellation {
263269
repeat(3) {
264270
requestChannel.send(
265271
HelloRequest.newBuilder()
266272
.setName(it.toString())
267273
.build()
268274
)
269-
delay(5)
275+
callState.client.cancelled.await()
270276
}
271277
}
272-
assertFailsWithStatus(Status.CANCELLED) {
273-
job.join()
274-
}
275278
}
279+
callState.client.onReady.await()
276280
cancel()
277281
}
278282
}
279283

280-
verify { rpcSpy.call.cancel(any(), any()) }
284+
callState.client.cancelled.assertBlocking { "Client must be cancelled" }
285+
286+
assertFailsWithCancellation {
287+
runBlocking { response.await().message }
288+
}
289+
290+
coVerify(exactly = 1) { requestChannel.send(any()) }
291+
verify(exactly = 1) { rpcSpy.call.cancel(any(), any()) }
281292
assert(requestChannel.isClosedForSend) { "Request channel should be closed for send" }
282293

283294
}
@@ -299,7 +310,7 @@ class ClientCallClientStreamingTests :
299310

300311
launch(Dispatchers.Default) {
301312
launch(start = CoroutineStart.UNDISPATCHED) {
302-
assertFailsWithStatus(Status.CANCELLED) {
313+
assertFailsWithCancellation {
303314
repeat(3) {
304315
requestChannel.send(
305316
HelloRequest.newBuilder()
@@ -313,14 +324,14 @@ class ClientCallClientStreamingTests :
313324
launch {
314325
error("cancel")
315326
}
316-
assertFailsWithStatus(Status.CANCELLED) {
327+
assertFailsWithCancellation {
317328
callChannel.response.await().message
318329
}
319330
}
320331
}
321332
}
322333

323-
verify { rpcSpy.call.cancel(any(), any()) }
334+
verify(exactly = 1) { rpcSpy.call.cancel(any(), any()) }
324335
assert(requestChannel.isClosedForSend) { "Request channel should be closed for send" }
325336

326337
}
@@ -348,7 +359,7 @@ class ClientCallClientStreamingTests :
348359
requestChannel.close(expectedException)
349360
}
350361

351-
assertFailsWithStatus2(Status.CANCELLED,"CANCELLED: $expectedCancelMessage"){
362+
assertFailsWithStatus(Status.CANCELLED,"CANCELLED: $expectedCancelMessage"){
352363
response.await()
353364
}
354365
}

0 commit comments

Comments
 (0)