Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor stub extension generator #75

Merged
merged 3 commits into from
Sep 2, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ _\*\*-\*\*_
* New: Update to project gradle to `5.6.2`

#### Protoc Plugin
* Fix: If no file filter is defined, fallback to `CodeGeneratorRequest.fileToGenerateList` [PR-70](https://github.com/marcoferrer/kroto-plus/pull/70)
* Fix: If no file filter is defined, fallback to `CodeGeneratorRequest.fileToGenerateList` [PR-70](https://github.com/marcoferrer/kroto-plus/pull/70)
* Fix: File filter is no longer ignored in stub extension generator


## Version 0.5.0-RC
Expand Down
3 changes: 2 additions & 1 deletion protoc-gen-kroto-plus/generator-tests/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ dependencies{
testImplementation project(':kroto-plus-message')
testImplementation project(':kroto-plus-coroutines')
testImplementation project(':kroto-plus-test')

testImplementation "io.mockk:mockk:${Versions.mockk}"

// For jdk 9+ you need to include javax.annotations
// The reason is outlined in this grpc issue
// https://github.com/grpc/grpc-java/issues/4725
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,51 +18,55 @@ package com.github.marcoferrer.krotoplus.generators

import com.github.marcoferrer.krotoplus.coroutines.launchProducerJob
import com.github.marcoferrer.krotoplus.coroutines.withCoroutineContext
import io.grpc.examples.helloworld.*
import io.grpc.examples.helloworld.GreeterCoroutineGrpc
import io.grpc.examples.helloworld.GreeterGrpc
import io.grpc.examples.helloworld.HelloReply
import io.grpc.examples.helloworld.HelloRequest
import io.grpc.examples.helloworld.sayHello
import io.grpc.examples.helloworld.sayHelloClientStreaming
import io.grpc.examples.helloworld.sayHelloServerStreaming
import io.grpc.examples.helloworld.sayHelloStreaming
import io.grpc.examples.helloworld.send
import io.grpc.stub.StreamObserver
import io.grpc.testing.GrpcServerRule
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.*
import io.mockk.spyk
import io.mockk.verify
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.ObsoleteCoroutinesApi
import kotlinx.coroutines.channels.ReceiveChannel
import kotlinx.coroutines.channels.SendChannel
import kotlinx.coroutines.channels.consumeEach
import kotlinx.coroutines.channels.receiveOrNull
import kotlinx.coroutines.channels.toList
import kotlinx.coroutines.runBlocking
import org.junit.Rule
import org.junit.Test
import java.util.concurrent.atomic.AtomicBoolean
import kotlin.coroutines.CoroutineContext
import kotlin.test.BeforeTest
import kotlin.test.assertEquals
import kotlin.test.assertNull

@UseExperimental(ExperimentalCoroutinesApi::class,ObsoleteCoroutinesApi::class)
@UseExperimental(ExperimentalCoroutinesApi::class)
class GrpcStubExtsGeneratorTests {

@[Rule JvmField]
var grpcServerRule = GrpcServerRule().directExecutor()

val expectedMessage = "result"

@BeforeTest
fun setupService(){
grpcServerRule.serviceRegistry.addService(object : GreeterCoroutineGrpc.GreeterImplBase(){

override val initialContext: CoroutineContext
get() = Dispatchers.Unconfined

override suspend fun sayHello(request: HelloRequest): HelloReply {
return HelloReply { message = expectedMessage }
}

override suspend fun sayHelloClientStreaming(requestChannel: ReceiveChannel<HelloRequest>): HelloReply {
return HelloReply {
message = requestChannel.toList().joinToString(separator = "|"){ it.name }
}
}

override suspend fun sayHelloServerStreaming(
request: HelloRequest,
responseChannel: SendChannel<HelloReply>
) {
repeat(3){
responseChannel.send { message = request.name + "-$it" }
}
}

override suspend fun sayHelloStreaming(
requestChannel: ReceiveChannel<HelloRequest>,
responseChannel: SendChannel<HelloReply>
Expand All @@ -77,46 +81,141 @@ class GrpcStubExtsGeneratorTests {
}

@Test
fun `Unary blocking exts are generated`(){
val stub = GreeterGrpc.newBlockingStub(grpcServerRule.channel)
assertEquals(expectedMessage,stub.sayHello(HelloRequest.getDefaultInstance()).message)
assertEquals(expectedMessage,stub.sayHello().message)
assertEquals(expectedMessage,stub.sayHello { name = "test" }.message)
fun `Client streaming coroutine exts are generated`() = runBlocking {
val stub = GreeterGrpc.newStub(grpcServerRule.channel)
.withCoroutineContext()

val (requestChannel, response) = stub.sayHelloClientStreaming()

launchProducerJob(requestChannel){
repeat(3){
send { name = "name $it" }
}
}
assertEquals("name 0|name 1|name 2",response.await().message)
}

@Test
fun `Unary future exts are generated`(){
val stub = GreeterGrpc.newFutureStub(grpcServerRule.channel)
assertEquals(expectedMessage,stub.sayHello(HelloRequest.getDefaultInstance()).get().message)
assertEquals(expectedMessage,stub.sayHello().get().message)
assertEquals(expectedMessage,stub.sayHello { name = "test" }.get().message)
fun `Bidi streaming coroutine exts are generated`() {
runBlocking {

val stub = GreeterGrpc.newStub(grpcServerRule.channel)
.withCoroutineContext()

val (requestChannel, responseChannel) = stub.sayHelloStreaming()

launchProducerJob(requestChannel) {
repeat(3) {
send { name = "name $it" }
}
}

val results = responseChannel.toList()
assertEquals(9, results.size)

val expected = "name 0|name 0|name 0" +
"|name 1|name 1|name 1" +
"|name 2|name 2|name 2"
assertEquals(
expected,
results.joinToString(separator = "|") { it.message }
)
}
}
}

@UseExperimental(ExperimentalCoroutinesApi::class)
class GrpcStubServerStreamingExtsGeneratorTests {

@[Rule JvmField]
var grpcServerRule = GrpcServerRule().directExecutor()

@BeforeTest
fun setupService(){
grpcServerRule.serviceRegistry.addService(object : GreeterCoroutineGrpc.GreeterImplBase(){
override val initialContext: CoroutineContext = Dispatchers.Unconfined
override suspend fun sayHelloServerStreaming(
request: HelloRequest,
responseChannel: SendChannel<HelloReply>
) {
repeat(3){
responseChannel.send { message = request.name + "-$it" }
}
}
})
}

@Test
fun `Unary coroutine exts are generated`() = runBlocking {
fun `Async stub exts for default arg are generated`() {
val responseObserver = spyk(TestObserver())
val stub = GreeterGrpc.newStub(grpcServerRule.channel)
assertEquals(expectedMessage,stub.sayHello(HelloRequest.getDefaultInstance()).message)
assertEquals(expectedMessage,stub.sayHello().message)
assertEquals(expectedMessage,stub.sayHello { name = "test" }.message)

stub.sayHelloServerStreaming(responseObserver)
while(!responseObserver.isCompleted.get()){}
repeat(3){ index ->
verify(exactly = 1) { responseObserver.onNext(match { it.message == "-$index" }) }
}
}

@Test
fun `Client streaming coroutine exts are generated`() = runBlocking {
fun `Async stub exts for lambda builders are generated`() {
val responseObserver = spyk(TestObserver())
val stub = GreeterGrpc.newStub(grpcServerRule.channel)
.withCoroutineContext()

val (requestChannel, response) = stub.sayHelloClientStreaming()
stub.sayHelloServerStreaming(responseObserver){ name = "test" }
while(!responseObserver.isCompleted.get()){}
repeat(3){ index ->
verify(exactly = 1) { responseObserver.onNext(match { it.message == "test-$index" }) }
}
}

launchProducerJob(requestChannel){
repeat(3){
send { name = "name $it" }
}
@Test
fun `Async stub exts for method signatures are generated`() {
val responseObserver = spyk(TestObserver())
val stub = GreeterGrpc.newStub(grpcServerRule.channel)

stub.sayHelloServerStreaming("test", responseObserver)
while(!responseObserver.isCompleted.get()){}
repeat(3){ index ->
verify(exactly = 1) { responseObserver.onNext(match { it.message == "test-$index" }) }
}
assertEquals("name 0|name 1|name 2",response.await().message)
}

@Test
fun `Server streaming coroutine exts are generated`() = runBlocking {
fun `Blocking stub default arg exts are generated`() {
val stub = GreeterGrpc.newBlockingStub(grpcServerRule.channel)

val result = stub.sayHelloServerStreaming().asSequence().toList()
assertEquals(3, result.size)
result.withIndex().forEach { (index, reply) ->
assertEquals("-$index",reply.message)
}
}

@Test
fun `Blocking stub lambda builder exts are generated`() {
val stub = GreeterGrpc.newBlockingStub(grpcServerRule.channel)

val result = stub.sayHelloServerStreaming { name = "with-arg" }.asSequence().toList()
assertEquals(3, result.size)
result.withIndex().forEach { (index, reply) ->
assertEquals("with-arg-$index",reply.message)
}
}

@Test
fun `Blocking stub method signature exts are generated`() {
val stub = GreeterGrpc.newBlockingStub(grpcServerRule.channel)

val result = stub.sayHelloServerStreaming(name = "with-arg").asSequence().toList()
assertEquals(3, result.size)
result.withIndex().forEach { (index, reply) ->
assertEquals("with-arg-$index",reply.message)
}
}

@Test
fun `Async stub coroutine exts are generated`() = runBlocking {
val stub = GreeterGrpc.newStub(grpcServerRule.channel)
.withCoroutineContext()

Expand All @@ -135,41 +234,113 @@ class GrpcStubExtsGeneratorTests {
}
assertNull(response2.receiveOrNull())
assert(response2.isClosedForReceive)
}

@Test
fun `Async stub coroutine lambda builder exts are generated`() = runBlocking {
val stub = GreeterGrpc.newStub(grpcServerRule.channel)
.withCoroutineContext()

// Message Builder Ext
val response3 = stub.sayHelloServerStreaming { name = "with-block" }
val response = stub.sayHelloServerStreaming { name = "with-block" }
repeat(3){
assertEquals("with-block-$it",response3.receive().message)
assertEquals("with-block-$it",response.receive().message)
}
assertNull(response3.receiveOrNull())
assert(response3.isClosedForReceive)
assertNull(response.receiveOrNull())
assert(response.isClosedForReceive)
}

@Test
fun `Bidi streaming coroutine exts are generated`() {
runBlocking {
fun `Async stub coroutine method signature exts are generated`() = runBlocking {
val stub = GreeterGrpc.newStub(grpcServerRule.channel)
.withCoroutineContext()

val stub = GreeterGrpc.newStub(grpcServerRule.channel)
.withCoroutineContext()
// Method signature Ext
val response = stub.sayHelloServerStreaming(name = "with-block")
repeat(3){
assertEquals("with-block-$it",response.receive().message)
}
assertNull(response.receiveOrNull())
assert(response.isClosedForReceive)
}
}

val (requestChannel, responseChannel) = stub.sayHelloStreaming()
class GrpcStubUnaryExtsGeneratorTests {

launchProducerJob(requestChannel) {
repeat(3) {
send { name = "name $it" }
}
@[Rule JvmField]
var grpcServerRule = GrpcServerRule().directExecutor()

@BeforeTest
fun setupService(){
grpcServerRule.serviceRegistry.addService(object : GreeterCoroutineGrpc.GreeterImplBase(){
override val initialContext: CoroutineContext = Dispatchers.Unconfined
override suspend fun sayHello(request: HelloRequest): HelloReply {
return HelloReply { message = "result-${request.name}" }
}
})
}

val results = responseChannel.toList()
assertEquals(9, results.size)
@Test
fun `Async stub exts for default arg are generated`() {
val responseObserver = spyk(TestObserver())
val stub = GreeterGrpc.newStub(grpcServerRule.channel)

val expected = "name 0|name 0|name 0" +
"|name 1|name 1|name 1" +
"|name 2|name 2|name 2"
assertEquals(
expected,
results.joinToString(separator = "|") { it.message }
)
}
stub.sayHello(responseObserver)
while(!responseObserver.isCompleted.get()){}
verify(exactly = 1) { responseObserver.onNext(match { it.message == "result-" }) }
}

@Test
fun `Async stub exts for lambda builders are generated`() {
val responseObserver = spyk(TestObserver())
val stub = GreeterGrpc.newStub(grpcServerRule.channel)

stub.sayHello(responseObserver){ name = "test" }
while(!responseObserver.isCompleted.get()){}
verify(exactly = 1) { responseObserver.onNext(match { it.message == "result-test" }) }
}

@Test
fun `Async stub exts for method signatures are generated`() {
val responseObserver = spyk(TestObserver())
val stub = GreeterGrpc.newStub(grpcServerRule.channel)

stub.sayHello("test", responseObserver)
while(!responseObserver.isCompleted.get()){}
verify(exactly = 1) { responseObserver.onNext(match { it.message == "result-test" }) }
}

@Test
fun `Blocking stub exts are generated`(){
val stub = GreeterGrpc.newBlockingStub(grpcServerRule.channel)
assertEquals("result-",stub.sayHello(HelloRequest.getDefaultInstance()).message)
assertEquals("result-",stub.sayHello().message)
assertEquals("result-test",stub.sayHello { name = "test" }.message)
assertEquals("result-test",stub.sayHello(name = "test").message)
}

@Test
fun `Future stub exts are generated`(){
val stub = GreeterGrpc.newFutureStub(grpcServerRule.channel)
assertEquals("result-",stub.sayHello(HelloRequest.getDefaultInstance()).get().message)
assertEquals("result-",stub.sayHello().get().message)
assertEquals("result-test",stub.sayHello { name = "test" }.get().message)
assertEquals("result-test",stub.sayHello(name = "test").get().message)
}

@Test
fun `Async stub coroutine exts are generated`() = runBlocking {
val stub = GreeterGrpc.newStub(grpcServerRule.channel)
assertEquals("result-",stub.sayHello(HelloRequest.getDefaultInstance()).message)
assertEquals("result-",stub.sayHello().message)
assertEquals("result-test",stub.sayHello { name = "test" }.message)
assertEquals("result-test",stub.sayHello(name = "test").message)
}
}

class TestObserver : StreamObserver<HelloReply> {
val isCompleted = AtomicBoolean()
override fun onNext(value: HelloReply?) {}
override fun onError(t: Throwable?) { isCompleted.set(true) }
override fun onCompleted() { isCompleted.set(true) }
}
Loading