From edb76665d018a2e4ddadb6ea3fd8c32799651bd2 Mon Sep 17 00:00:00 2001 From: Mantas Gridinas Date: Thu, 31 Aug 2017 15:31:18 +0300 Subject: [PATCH 1/8] added async implementation that uses kotlin coroutines --- async/build.gradle | 41 +++++++ async/gradle.properties | 3 + async/readme.md | 4 + .../chatty/v3/async/main/AsyncClient.kt | 50 ++++++++ .../chatty/v3/async/route/AsyncController.kt | 36 ++++++ .../chatty/v3/async/route/AsyncRoute.kt | 114 ++++++++++++++++++ .../chatty/v3/async/route/AsyncRouter.kt | 26 ++++ .../saltyjuice/dragas/chatty/v3/async/Main.kt | 5 + .../async/mock/controller/MockController.kt | 32 +++++ .../chatty/v3/async/mock/io/MockAdapter.kt | 17 +++ .../chatty/v3/async/mock/io/MockEndpoint.kt | 19 +++ .../chatty/v3/async/mock/main/MockClient.kt | 25 ++++ .../chatty/v3/async/mock/route/MockRoute.kt | 65 ++++++++++ .../chatty/v3/async/mock/route/MockRouter.kt | 12 ++ .../chatty/v3/async/unit/ControllerTest.kt | 48 ++++++++ .../dragas/chatty/v3/async/unit/RouteTest.kt | 96 +++++++++++++++ settings.gradle | 1 + 17 files changed, 594 insertions(+) create mode 100644 async/build.gradle create mode 100644 async/gradle.properties create mode 100644 async/readme.md create mode 100644 async/src/main/kotlin/lt/saltyjuice/dragas/chatty/v3/async/main/AsyncClient.kt create mode 100644 async/src/main/kotlin/lt/saltyjuice/dragas/chatty/v3/async/route/AsyncController.kt create mode 100644 async/src/main/kotlin/lt/saltyjuice/dragas/chatty/v3/async/route/AsyncRoute.kt create mode 100644 async/src/main/kotlin/lt/saltyjuice/dragas/chatty/v3/async/route/AsyncRouter.kt create mode 100644 async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/Main.kt create mode 100644 async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/controller/MockController.kt create mode 100644 async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/io/MockAdapter.kt create mode 100644 async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/io/MockEndpoint.kt create mode 100644 async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/main/MockClient.kt create mode 100644 async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/route/MockRoute.kt create mode 100644 async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/route/MockRouter.kt create mode 100644 async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/unit/ControllerTest.kt create mode 100644 async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/unit/RouteTest.kt diff --git a/async/build.gradle b/async/build.gradle new file mode 100644 index 0000000..4698e1a --- /dev/null +++ b/async/build.gradle @@ -0,0 +1,41 @@ +group 'lt.saltyjuice.dragas' +version '3.0.0' + +buildscript { + ext.kotlin_version = '1.1.4-2' + + repositories { + mavenCentral() + } + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } +} + +apply plugin: 'java' +apply plugin: 'kotlin' + +sourceCompatibility = 1.8 + +repositories { + mavenCentral() + maven { url "https://oss.sonatype.org/content/repositories/snapshots" } + maven { url "https://oss.sonatype.org/content/repositories/releases" } + jcenter() +} + +dependencies { + compile "org.jetbrains.kotlin:kotlin-stdlib-jre8:$kotlin_version" + compile "lt.saltyjuice.dragas:chatty-core:3.0.0" + compile 'org.jetbrains.kotlinx:kotlinx-coroutines-core:0.18' + testCompile group: 'junit', name: 'junit', version: '4.12' +} + +compileKotlin { + kotlinOptions.jvmTarget = "1.8" +} +compileTestKotlin { + kotlinOptions.jvmTarget = "1.8" +} + +apply from: "../upload.gradle" \ No newline at end of file diff --git a/async/gradle.properties b/async/gradle.properties new file mode 100644 index 0000000..420eebe --- /dev/null +++ b/async/gradle.properties @@ -0,0 +1,3 @@ +mProjectName="chatty-async" +mArchiveBaseName=chatty-async +mProjectDescription="Chatty Async implementation" \ No newline at end of file diff --git a/async/readme.md b/async/readme.md new file mode 100644 index 0000000..18b87b5 --- /dev/null +++ b/async/readme.md @@ -0,0 +1,4 @@ +# Chatty/Async + +Package meant to implement asynchronous behavior for Chatty bots. Currently depends on +`chatty-core:3.0.0` \ No newline at end of file diff --git a/async/src/main/kotlin/lt/saltyjuice/dragas/chatty/v3/async/main/AsyncClient.kt b/async/src/main/kotlin/lt/saltyjuice/dragas/chatty/v3/async/main/AsyncClient.kt new file mode 100644 index 0000000..c517c7d --- /dev/null +++ b/async/src/main/kotlin/lt/saltyjuice/dragas/chatty/v3/async/main/AsyncClient.kt @@ -0,0 +1,50 @@ +package lt.saltyjuice.dragas.chatty.v3.async.main + +import kotlinx.coroutines.experimental.CommonPool +import kotlinx.coroutines.experimental.Job +import kotlinx.coroutines.experimental.channels.Channel +import kotlinx.coroutines.experimental.channels.consumeEach +import kotlinx.coroutines.experimental.launch +import lt.saltyjuice.dragas.chatty.v3.async.route.AsyncController +import lt.saltyjuice.dragas.chatty.v3.async.route.AsyncRouter +import lt.saltyjuice.dragas.chatty.v3.core.main.Client +import lt.saltyjuice.dragas.chatty.v3.core.route.Controller + +/** + * Asynchronous implementation of [Client]. + * + * Supports using both [Controller] and [AsyncController]. + * + * This implementation provides an additional field - [responseChannel], which is used to listen for responses + * generated by triggered routes. + */ +abstract class AsyncClient : Client() +{ + /** + * Used to build [AsyncRouter] + */ + protected open val responseChannel = Channel(Channel.UNLIMITED) + /** + * Holds reference to all listener jobs provided by this client. + */ + protected open val listenerJobs: ArrayList = ArrayList() + /** + * Holds the count of how many listeners for responses the should be at most. + */ + protected open val mostJobs: Int = Runtime.getRuntime().availableProcessors() + override abstract val router: AsyncRouter + + override fun onConnect() + { + repeat(Math.min(Runtime.getRuntime().availableProcessors(), mostJobs)) + { + listenerJobs.add(launch(CommonPool) { responseChannel.consumeEach(this@AsyncClient::writeResponse) }) + } + } + + override fun onDisconnect() + { + listenerJobs.forEach { it.cancel() } + listenerJobs.clear() + } +} \ No newline at end of file diff --git a/async/src/main/kotlin/lt/saltyjuice/dragas/chatty/v3/async/route/AsyncController.kt b/async/src/main/kotlin/lt/saltyjuice/dragas/chatty/v3/async/route/AsyncController.kt new file mode 100644 index 0000000..4b8e7e2 --- /dev/null +++ b/async/src/main/kotlin/lt/saltyjuice/dragas/chatty/v3/async/route/AsyncController.kt @@ -0,0 +1,36 @@ +package lt.saltyjuice.dragas.chatty.v3.async.route + +import kotlinx.coroutines.experimental.CommonPool +import kotlinx.coroutines.experimental.channels.SendChannel +import kotlinx.coroutines.experimental.launch +import lt.saltyjuice.dragas.chatty.v3.core.route.Controller + +open class AsyncController : Controller() +{ + private var listener: SendChannel? = null + + /** + * Equivalent to calling writeResponse(response, false) + */ + override fun writeResponse(response: Response) + { + writeResponse(response, false) + } + + /** + * Writes response to response listener, when [now] is true. Otherwise, response + * is written to internal buffer which is later consumed and cleaned out. + */ + open fun writeResponse(response: Response, now: Boolean) + { + if (now) + launch(CommonPool) { listener!!.send(response) } + else + super.writeResponse(response) + } + + open fun listen(listener: SendChannel) + { + this.listener = listener + } +} \ No newline at end of file diff --git a/async/src/main/kotlin/lt/saltyjuice/dragas/chatty/v3/async/route/AsyncRoute.kt b/async/src/main/kotlin/lt/saltyjuice/dragas/chatty/v3/async/route/AsyncRoute.kt new file mode 100644 index 0000000..b9f2870 --- /dev/null +++ b/async/src/main/kotlin/lt/saltyjuice/dragas/chatty/v3/async/route/AsyncRoute.kt @@ -0,0 +1,114 @@ +package lt.saltyjuice.dragas.chatty.v3.async.route + +import kotlinx.coroutines.experimental.CommonPool +import kotlinx.coroutines.experimental.channels.SendChannel +import kotlinx.coroutines.experimental.channels.actor +import kotlinx.coroutines.experimental.launch +import lt.saltyjuice.dragas.chatty.v3.core.middleware.AfterMiddleware +import lt.saltyjuice.dragas.chatty.v3.core.middleware.BeforeMiddleware +import lt.saltyjuice.dragas.chatty.v3.core.route.Controller +import lt.saltyjuice.dragas.chatty.v3.core.route.Route +import java.lang.reflect.Method + +/** + * + * Asynchronous route, that implements techniques to asynchronously receive responses as well as + * + * @see Route + */ +open class AsyncRoute : Route() +{ + protected open lateinit var responseChannel: SendChannel + protected open val listener = actor(CommonPool) + { + for (response in channel) + this@AsyncRoute.attemptRespond(response) + } + + override fun getControllerInstance(): Controller + { + return super.getControllerInstance().apply { if (this is AsyncController) listen(listener) } + } + + /** + * Tests [response] if it can be responded with and pushes it to [responseChannel] + */ + override fun attemptRespond(response: Response) + { + launch(CommonPool) + { + if (canRespond(response)) responseChannel.send(response) + } + } + + /** + * In case of application shut down, this method should be called to clean this route's listener. + */ + open fun close() + { + listener.cancel() + } + + /** + * Returns an empty list, since it isn't necessary anymore. + */ + override fun getResponses(): List + { + return listOf() + } + + abstract class Builder : Route.Builder() + { + protected open var mChannel: SendChannel? = null + + abstract override fun returnableRoute(): AsyncRoute + + override fun adapt(route: Route): AsyncRoute + { + val adapted = super.adapt(route) as AsyncRoute + adapted.responseChannel = this.mChannel!! + return adapted + } + + override fun after(clazz: Class>): AsyncRoute.Builder + { + return super.after(clazz) as AsyncRoute.Builder + } + + override fun before(clazz: Class>): AsyncRoute.Builder + { + return super.before(clazz) as AsyncRoute.Builder + } + + override fun callback(callback: (Route, Request) -> Unit): AsyncRoute.Builder + { + return super.callback(callback) as AsyncRoute.Builder + } + + override fun consume(controller: Class>, method: Method): AsyncRoute.Builder + { + return super.consume(controller, method) as AsyncRoute.Builder + } + + override fun controller(clazz: Class>): AsyncRoute.Builder + { + return super.controller(clazz) as AsyncRoute.Builder + } + + override fun description(string: String): AsyncRoute.Builder + { + return super.description(string) as AsyncRoute.Builder + } + + override fun testCallback(callback: (Route, Request) -> Boolean): AsyncRoute.Builder + { + return super.testCallback(callback) as AsyncRoute.Builder + } + + open fun responseChannel(channel: SendChannel): AsyncRoute.Builder + { + this.mChannel = channel + return this + } + } +} \ No newline at end of file diff --git a/async/src/main/kotlin/lt/saltyjuice/dragas/chatty/v3/async/route/AsyncRouter.kt b/async/src/main/kotlin/lt/saltyjuice/dragas/chatty/v3/async/route/AsyncRouter.kt new file mode 100644 index 0000000..d5d6784 --- /dev/null +++ b/async/src/main/kotlin/lt/saltyjuice/dragas/chatty/v3/async/route/AsyncRouter.kt @@ -0,0 +1,26 @@ +package lt.saltyjuice.dragas.chatty.v3.async.route + +import kotlinx.coroutines.experimental.channels.SendChannel +import lt.saltyjuice.dragas.chatty.v3.core.route.Route +import lt.saltyjuice.dragas.chatty.v3.core.route.Router + +/** + * Asynchronous implementation of core router. The difference is that you need to provide a [responseChannel], + * which is provided to all [AsyncRoute] based routes, so that they could send back generated responses at any time. + * + * @see Router + */ +abstract class AsyncRouter(protected open val responseChannel: SendChannel) : Router() +{ + abstract override fun builder(): AsyncRoute.Builder + + /** + * Any routes being added should use this method instead of [add(Route)], as it appends [responseChannel] to that route. + */ + override fun add(route: Route.Builder) + { + if (route is AsyncRoute.Builder) + route.responseChannel(responseChannel) + super.add(route) + } +} \ No newline at end of file diff --git a/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/Main.kt b/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/Main.kt new file mode 100644 index 0000000..8ede0f7 --- /dev/null +++ b/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/Main.kt @@ -0,0 +1,5 @@ +package lt.saltyjuice.dragas.chatty.v3.async + +class Main +{ +} \ No newline at end of file diff --git a/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/controller/MockController.kt b/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/controller/MockController.kt new file mode 100644 index 0000000..d929aa7 --- /dev/null +++ b/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/controller/MockController.kt @@ -0,0 +1,32 @@ +package lt.saltyjuice.dragas.chatty.v3.async.mock.controller + +import lt.saltyjuice.dragas.chatty.v3.async.route.AsyncController +import lt.saltyjuice.dragas.chatty.v3.core.route.On +import lt.saltyjuice.dragas.chatty.v3.core.route.When + +class MockController : AsyncController() +{ + @On(Int::class) + @When("isEven") + fun provideModuloOfTwoWritesLater(request: Int) + { + writeResponse(request % 2) + } + + fun isEven(request: Int): Boolean + { + return request % 2 == 0 + } + + @On(Int::class) + @When("isOdd") + fun providesModuloOfTwoWritesNow(request: Int) + { + writeResponse(request % 2, true) + } + + fun isOdd(request: Int): Boolean + { + return request % 2 != 0 + } +} \ No newline at end of file diff --git a/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/io/MockAdapter.kt b/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/io/MockAdapter.kt new file mode 100644 index 0000000..3ef5aa0 --- /dev/null +++ b/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/io/MockAdapter.kt @@ -0,0 +1,17 @@ +package lt.saltyjuice.dragas.chatty.v3.async.mock.io + +import lt.saltyjuice.dragas.chatty.v3.core.adapter.Deserializer +import lt.saltyjuice.dragas.chatty.v3.core.adapter.Serializer + +class MockAdapter : Serializer, Deserializer +{ + override fun serialize(any: Int): Int + { + return any + } + + override fun deserialize(block: Int): Int + { + return block + } +} \ No newline at end of file diff --git a/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/io/MockEndpoint.kt b/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/io/MockEndpoint.kt new file mode 100644 index 0000000..0896f04 --- /dev/null +++ b/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/io/MockEndpoint.kt @@ -0,0 +1,19 @@ +package lt.saltyjuice.dragas.chatty.v3.async.mock.io + +import lt.saltyjuice.dragas.chatty.v3.core.io.Input +import lt.saltyjuice.dragas.chatty.v3.core.io.Output + +class MockEndpoint : Input, Output +{ + override val adapter: MockAdapter = MockAdapter() + + override fun getRequest(): Int + { + return 5 + } + + override fun writeResponse(response: Int) + { + + } +} \ No newline at end of file diff --git a/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/main/MockClient.kt b/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/main/MockClient.kt new file mode 100644 index 0000000..fae99dc --- /dev/null +++ b/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/main/MockClient.kt @@ -0,0 +1,25 @@ +package lt.saltyjuice.dragas.chatty.v3.async.mock.main + +import lt.saltyjuice.dragas.chatty.v3.async.main.AsyncClient +import lt.saltyjuice.dragas.chatty.v3.async.mock.route.MockRouter +import lt.saltyjuice.dragas.chatty.v3.core.io.Input +import lt.saltyjuice.dragas.chatty.v3.core.io.Output + +class MockClient : AsyncClient() +{ + override val sout: Output + get() = TODO("not implemented") //To change initializer of created properties use File | Settings | File Templates. + override val router: MockRouter = MockRouter(responseChannel) + override val sin: Input + get() = TODO("not implemented") //To change initializer of created properties use File | Settings | File Templates. + + override fun connect(): Boolean + { + return true + } + + override fun isConnected(): Boolean + { + return true + } +} \ No newline at end of file diff --git a/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/route/MockRoute.kt b/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/route/MockRoute.kt new file mode 100644 index 0000000..439ca29 --- /dev/null +++ b/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/route/MockRoute.kt @@ -0,0 +1,65 @@ +package lt.saltyjuice.dragas.chatty.v3.async.mock.route + +import kotlinx.coroutines.experimental.channels.SendChannel +import lt.saltyjuice.dragas.chatty.v3.async.route.AsyncRoute +import lt.saltyjuice.dragas.chatty.v3.core.middleware.AfterMiddleware +import lt.saltyjuice.dragas.chatty.v3.core.middleware.BeforeMiddleware +import lt.saltyjuice.dragas.chatty.v3.core.route.Controller +import lt.saltyjuice.dragas.chatty.v3.core.route.Route +import java.lang.reflect.Method + +open class MockRoute : AsyncRoute() +{ + open class Builder : AsyncRoute.Builder() + { + override fun returnableRoute(): MockRoute + { + return MockRoute() + } + + override fun adapt(route: Route): MockRoute + { + return super.adapt(route) as MockRoute + } + + override fun after(clazz: Class>): Builder + { + return super.after(clazz) as Builder + } + + override fun before(clazz: Class>): Builder + { + return super.before(clazz) as Builder + } + + override fun callback(callback: (Route, Int) -> Unit): Builder + { + return super.callback(callback) as Builder + } + + override fun consume(controller: Class>, method: Method): Builder + { + return super.consume(controller, method) as Builder + } + + override fun controller(clazz: Class>): Builder + { + return super.controller(clazz) as Builder + } + + override fun description(string: String): Builder + { + return super.description(string) as Builder + } + + override fun testCallback(callback: (Route, Int) -> Boolean): Builder + { + return super.testCallback(callback) as Builder + } + + override fun responseChannel(channel: SendChannel): Builder + { + return super.responseChannel(channel) as Builder + } + } +} \ No newline at end of file diff --git a/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/route/MockRouter.kt b/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/route/MockRouter.kt new file mode 100644 index 0000000..2313215 --- /dev/null +++ b/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/route/MockRouter.kt @@ -0,0 +1,12 @@ +package lt.saltyjuice.dragas.chatty.v3.async.mock.route + +import kotlinx.coroutines.experimental.channels.SendChannel +import lt.saltyjuice.dragas.chatty.v3.async.route.AsyncRouter + +class MockRouter(channel: SendChannel) : AsyncRouter(channel) +{ + override fun builder(): MockRoute.Builder + { + return MockRoute.Builder() + } +} \ No newline at end of file diff --git a/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/unit/ControllerTest.kt b/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/unit/ControllerTest.kt new file mode 100644 index 0000000..f56ccf5 --- /dev/null +++ b/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/unit/ControllerTest.kt @@ -0,0 +1,48 @@ +package lt.saltyjuice.dragas.chatty.v3.async.unit + +import kotlinx.coroutines.experimental.channels.Channel +import kotlinx.coroutines.experimental.runBlocking +import lt.saltyjuice.dragas.chatty.v3.async.mock.controller.MockController +import org.junit.* +import org.junit.rules.Timeout +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import java.util.concurrent.TimeUnit + +@RunWith(JUnit4::class) +open class ControllerTest +{ + @Rule + @JvmField + public val timeout = Timeout(1000, TimeUnit.MILLISECONDS) + + @Test + fun controllerWritesResponseNow() = runBlocking() + { + controller.providesModuloOfTwoWritesNow(2) + Assert.assertEquals(0, channel.receive()) + } + + companion object + { + @JvmStatic + private val controller: MockController = MockController() + + @JvmStatic + private val channel: Channel = Channel() + + @JvmStatic + @BeforeClass + fun init() + { + controller.listen(channel) + } + + @JvmStatic + @AfterClass + fun destroy() + { + channel.close() + } + } +} \ No newline at end of file diff --git a/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/unit/RouteTest.kt b/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/unit/RouteTest.kt new file mode 100644 index 0000000..61b175a --- /dev/null +++ b/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/unit/RouteTest.kt @@ -0,0 +1,96 @@ +package lt.saltyjuice.dragas.chatty.v3.async.unit + +import kotlinx.coroutines.experimental.channels.Channel +import kotlinx.coroutines.experimental.runBlocking +import lt.saltyjuice.dragas.chatty.v3.async.mock.controller.MockController +import lt.saltyjuice.dragas.chatty.v3.async.mock.route.MockRoute +import org.junit.AfterClass +import org.junit.Assert +import org.junit.Rule +import org.junit.Test +import org.junit.rules.Timeout +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import java.util.concurrent.TimeUnit + +@RunWith(JUnit4::class) +class RouteTest +{ + @Rule + @JvmField + public val timeout = Timeout(1000, TimeUnit.MILLISECONDS) + + @Test + fun routeRespondsAsynchronously() = runBlocking() + { + buildAsyncRoute().attemptTrigger(3) + Assert.assertEquals(1, channel.receive()) + } + + @Test + fun routeRespondsSynchonously() = runBlocking() + { + buildSyncRoute().attemptTrigger(2) + Assert.assertEquals(0, channel.receive()) + } + + companion object + { + @JvmStatic + private val channel: Channel = Channel() + + @JvmStatic + private val builder: MockRoute.Builder by lazy() + { + MockRoute.Builder().apply() + { + controller(MockController::class.java) + responseChannel(channel) + singleton(false) + } + } + + @JvmStatic + fun buildAsyncRoute(): MockRoute + { + return builder.apply() + { + testCallback() + { route, i -> + val controller = route.getControllerInstance() as MockController + controller.isOdd(i) + } + callback() + { route, i -> + val controller = route.getControllerInstance() as MockController + controller.providesModuloOfTwoWritesNow(i) + } + }.build() as MockRoute + } + + @JvmStatic + fun buildSyncRoute(): MockRoute + { + return builder.apply() + { + testCallback() + { route, i -> + val controller = route.getControllerInstance() as MockController + controller.isEven(i) + } + callback() + { route, i -> + val controller = route.getControllerInstance() as MockController + controller.providesModuloOfTwoWritesNow(i) + } + }.build() as MockRoute + } + + @JvmStatic + @AfterClass + fun destroy() + { + channel.close() + } + } +} \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index e04535b..8e741aa 100644 --- a/settings.gradle +++ b/settings.gradle @@ -5,4 +5,5 @@ include ':birc' include ":websocket" include ':discord' include ':biscord' +include 'async' From a9dc76fc974c82a53500fdf51602c4b56724a01b Mon Sep 17 00:00:00 2001 From: Dragas Date: Sat, 2 Sep 2017 08:27:12 +0300 Subject: [PATCH 2/8] redundant main --- .../test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/Main.kt | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/Main.kt diff --git a/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/Main.kt b/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/Main.kt deleted file mode 100644 index 8ede0f7..0000000 --- a/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/Main.kt +++ /dev/null @@ -1,5 +0,0 @@ -package lt.saltyjuice.dragas.chatty.v3.async - -class Main -{ -} \ No newline at end of file From d43eaaf09ed7c7ebfd13186043852b76ec5d67f8 Mon Sep 17 00:00:00 2001 From: Dragas Date: Sat, 2 Sep 2017 08:47:55 +0300 Subject: [PATCH 3/8] tests if router throws on not async controllers. --- .../mock/controller/NotAsyncController.kt | 20 +++++++ .../dragas/chatty/v3/async/unit/RouterTest.kt | 52 +++++++++++++++++++ 2 files changed, 72 insertions(+) create mode 100644 async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/controller/NotAsyncController.kt create mode 100644 async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/unit/RouterTest.kt diff --git a/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/controller/NotAsyncController.kt b/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/controller/NotAsyncController.kt new file mode 100644 index 0000000..d24b754 --- /dev/null +++ b/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/controller/NotAsyncController.kt @@ -0,0 +1,20 @@ +package lt.saltyjuice.dragas.chatty.v3.async.mock.controller + +import lt.saltyjuice.dragas.chatty.v3.core.route.Controller +import lt.saltyjuice.dragas.chatty.v3.core.route.On +import lt.saltyjuice.dragas.chatty.v3.core.route.When + +class NotAsyncController : Controller() +{ + @On(Int::class) + @When("isEven") + fun onEven(request: Int) + { + writeResponse(request) + } + + fun isEven(request: Int): Boolean + { + return request % 2 == 0 + } +} \ No newline at end of file diff --git a/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/unit/RouterTest.kt b/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/unit/RouterTest.kt new file mode 100644 index 0000000..2f835af --- /dev/null +++ b/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/unit/RouterTest.kt @@ -0,0 +1,52 @@ +package lt.saltyjuice.dragas.chatty.v3.async.unit + +import kotlinx.coroutines.experimental.channels.Channel +import lt.saltyjuice.dragas.chatty.v3.async.mock.controller.NotAsyncController +import lt.saltyjuice.dragas.chatty.v3.async.mock.route.MockRouter +import lt.saltyjuice.dragas.chatty.v3.core.exception.RouteBuilderException +import org.junit.Assert +import org.junit.BeforeClass +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +@RunWith(JUnit4::class) +class RouterTest +{ + @Test + fun routerCantBuildNotAsyncRoute() + { + shouldThrow(RouteBuilderException::class.java) + { + router.consume(NotAsyncController::class.java) + } + } + + fun shouldThrow(vararg clazz: Class, test: (() -> Unit)) + { + var threw = false + try + { + test() + } + catch (err: Throwable) + { + clazz.forEach { if (it.isAssignableFrom(err.javaClass)) threw = true } + } + + Assert.assertTrue(threw) + } + + companion object + { + private lateinit var router: MockRouter + private val channel = Channel() + private val controller = NotAsyncController::class.java + @BeforeClass + @JvmStatic + fun init() + { + router = MockRouter(channel) + } + } +} \ No newline at end of file From fdf9a34359fde479bbfa9ac8c28f240346d16e54 Mon Sep 17 00:00:00 2001 From: Dragas Date: Sat, 2 Sep 2017 10:29:50 +0300 Subject: [PATCH 4/8] refactored tests to use floats as response --- .../async/mock/controller/MockController.kt | 6 ++--- .../mock/controller/NotAsyncController.kt | 4 +-- .../chatty/v3/async/mock/io/MockAdapter.kt | 4 +-- .../chatty/v3/async/mock/io/MockEndpoint.kt | 12 +++++---- .../chatty/v3/async/mock/main/MockClient.kt | 27 ++++++++++++++----- .../chatty/v3/async/mock/route/MockRoute.kt | 18 ++++++------- .../chatty/v3/async/mock/route/MockRouter.kt | 2 +- .../chatty/v3/async/unit/ControllerTest.kt | 4 +-- .../dragas/chatty/v3/async/unit/RouteTest.kt | 2 +- .../dragas/chatty/v3/async/unit/RouterTest.kt | 2 +- 10 files changed, 48 insertions(+), 33 deletions(-) diff --git a/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/controller/MockController.kt b/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/controller/MockController.kt index d929aa7..3a179e7 100644 --- a/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/controller/MockController.kt +++ b/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/controller/MockController.kt @@ -4,13 +4,13 @@ import lt.saltyjuice.dragas.chatty.v3.async.route.AsyncController import lt.saltyjuice.dragas.chatty.v3.core.route.On import lt.saltyjuice.dragas.chatty.v3.core.route.When -class MockController : AsyncController() +class MockController : AsyncController() { @On(Int::class) @When("isEven") fun provideModuloOfTwoWritesLater(request: Int) { - writeResponse(request % 2) + writeResponse((request % 2).toFloat()) } fun isEven(request: Int): Boolean @@ -22,7 +22,7 @@ class MockController : AsyncController() @When("isOdd") fun providesModuloOfTwoWritesNow(request: Int) { - writeResponse(request % 2, true) + writeResponse((request % 2).toFloat(), true) } fun isOdd(request: Int): Boolean diff --git a/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/controller/NotAsyncController.kt b/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/controller/NotAsyncController.kt index d24b754..0d52daf 100644 --- a/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/controller/NotAsyncController.kt +++ b/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/controller/NotAsyncController.kt @@ -4,13 +4,13 @@ import lt.saltyjuice.dragas.chatty.v3.core.route.Controller import lt.saltyjuice.dragas.chatty.v3.core.route.On import lt.saltyjuice.dragas.chatty.v3.core.route.When -class NotAsyncController : Controller() +class NotAsyncController : Controller() { @On(Int::class) @When("isEven") fun onEven(request: Int) { - writeResponse(request) + writeResponse(request.toFloat()) } fun isEven(request: Int): Boolean diff --git a/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/io/MockAdapter.kt b/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/io/MockAdapter.kt index 3ef5aa0..afea2a9 100644 --- a/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/io/MockAdapter.kt +++ b/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/io/MockAdapter.kt @@ -3,9 +3,9 @@ package lt.saltyjuice.dragas.chatty.v3.async.mock.io import lt.saltyjuice.dragas.chatty.v3.core.adapter.Deserializer import lt.saltyjuice.dragas.chatty.v3.core.adapter.Serializer -class MockAdapter : Serializer, Deserializer +class MockAdapter : Serializer, Deserializer { - override fun serialize(any: Int): Int + override fun serialize(any: Float): Float { return any } diff --git a/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/io/MockEndpoint.kt b/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/io/MockEndpoint.kt index 0896f04..9a0ca14 100644 --- a/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/io/MockEndpoint.kt +++ b/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/io/MockEndpoint.kt @@ -1,19 +1,21 @@ package lt.saltyjuice.dragas.chatty.v3.async.mock.io +import kotlinx.coroutines.experimental.channels.Channel +import kotlinx.coroutines.experimental.runBlocking import lt.saltyjuice.dragas.chatty.v3.core.io.Input import lt.saltyjuice.dragas.chatty.v3.core.io.Output -class MockEndpoint : Input, Output +class MockEndpoint(private val requestChannel: Channel, private val responseChannel: Channel) : Input, Output { override val adapter: MockAdapter = MockAdapter() - override fun getRequest(): Int + override fun getRequest(): Int = runBlocking() { - return 5 + requestChannel.receive() } - override fun writeResponse(response: Int) + override fun writeResponse(response: Float) = runBlocking() { - + responseChannel.send(response) } } \ No newline at end of file diff --git a/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/main/MockClient.kt b/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/main/MockClient.kt index fae99dc..d3fd086 100644 --- a/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/main/MockClient.kt +++ b/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/main/MockClient.kt @@ -1,17 +1,30 @@ package lt.saltyjuice.dragas.chatty.v3.async.mock.main import lt.saltyjuice.dragas.chatty.v3.async.main.AsyncClient +import lt.saltyjuice.dragas.chatty.v3.async.mock.controller.MockController +import lt.saltyjuice.dragas.chatty.v3.async.mock.io.MockEndpoint import lt.saltyjuice.dragas.chatty.v3.async.mock.route.MockRouter -import lt.saltyjuice.dragas.chatty.v3.core.io.Input -import lt.saltyjuice.dragas.chatty.v3.core.io.Output +import lt.saltyjuice.dragas.chatty.v3.core.route.UsesControllers -class MockClient : AsyncClient() +@UsesControllers(MockController::class) +class MockClient(endpoint: MockEndpoint) : AsyncClient() { - override val sout: Output - get() = TODO("not implemented") //To change initializer of created properties use File | Settings | File Templates. + override val sout: MockEndpoint = endpoint override val router: MockRouter = MockRouter(responseChannel) - override val sin: Input - get() = TODO("not implemented") //To change initializer of created properties use File | Settings | File Templates. + override val sin: MockEndpoint = endpoint + + /*override fun onConnect() + { + launch(CommonPool) + { + writeResponse(2f) + } + }*/ + + /*override fun writeResponse(response: Float) + { + super.writeResponse(response) + }*/ override fun connect(): Boolean { diff --git a/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/route/MockRoute.kt b/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/route/MockRoute.kt index 439ca29..5066a19 100644 --- a/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/route/MockRoute.kt +++ b/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/route/MockRoute.kt @@ -8,21 +8,21 @@ import lt.saltyjuice.dragas.chatty.v3.core.route.Controller import lt.saltyjuice.dragas.chatty.v3.core.route.Route import java.lang.reflect.Method -open class MockRoute : AsyncRoute() +open class MockRoute : AsyncRoute() { - open class Builder : AsyncRoute.Builder() + open class Builder : AsyncRoute.Builder() { override fun returnableRoute(): MockRoute { return MockRoute() } - override fun adapt(route: Route): MockRoute + override fun adapt(route: Route): MockRoute { return super.adapt(route) as MockRoute } - override fun after(clazz: Class>): Builder + override fun after(clazz: Class>): Builder { return super.after(clazz) as Builder } @@ -32,17 +32,17 @@ open class MockRoute : AsyncRoute() return super.before(clazz) as Builder } - override fun callback(callback: (Route, Int) -> Unit): Builder + override fun callback(callback: (Route, Int) -> Unit): Builder { return super.callback(callback) as Builder } - override fun consume(controller: Class>, method: Method): Builder + override fun consume(controller: Class>, method: Method): Builder { return super.consume(controller, method) as Builder } - override fun controller(clazz: Class>): Builder + override fun controller(clazz: Class>): Builder { return super.controller(clazz) as Builder } @@ -52,12 +52,12 @@ open class MockRoute : AsyncRoute() return super.description(string) as Builder } - override fun testCallback(callback: (Route, Int) -> Boolean): Builder + override fun testCallback(callback: (Route, Int) -> Boolean): Builder { return super.testCallback(callback) as Builder } - override fun responseChannel(channel: SendChannel): Builder + override fun responseChannel(channel: SendChannel): Builder { return super.responseChannel(channel) as Builder } diff --git a/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/route/MockRouter.kt b/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/route/MockRouter.kt index 2313215..bbe58ca 100644 --- a/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/route/MockRouter.kt +++ b/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/route/MockRouter.kt @@ -3,7 +3,7 @@ package lt.saltyjuice.dragas.chatty.v3.async.mock.route import kotlinx.coroutines.experimental.channels.SendChannel import lt.saltyjuice.dragas.chatty.v3.async.route.AsyncRouter -class MockRouter(channel: SendChannel) : AsyncRouter(channel) +class MockRouter(channel: SendChannel) : AsyncRouter(channel) { override fun builder(): MockRoute.Builder { diff --git a/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/unit/ControllerTest.kt b/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/unit/ControllerTest.kt index f56ccf5..7eb98c4 100644 --- a/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/unit/ControllerTest.kt +++ b/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/unit/ControllerTest.kt @@ -20,7 +20,7 @@ open class ControllerTest fun controllerWritesResponseNow() = runBlocking() { controller.providesModuloOfTwoWritesNow(2) - Assert.assertEquals(0, channel.receive()) + Assert.assertEquals(0f, channel.receive()) } companion object @@ -29,7 +29,7 @@ open class ControllerTest private val controller: MockController = MockController() @JvmStatic - private val channel: Channel = Channel() + private val channel: Channel = Channel() @JvmStatic @BeforeClass diff --git a/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/unit/RouteTest.kt b/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/unit/RouteTest.kt index 61b175a..900164f 100644 --- a/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/unit/RouteTest.kt +++ b/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/unit/RouteTest.kt @@ -37,7 +37,7 @@ class RouteTest companion object { @JvmStatic - private val channel: Channel = Channel() + private val channel: Channel = Channel() @JvmStatic private val builder: MockRoute.Builder by lazy() diff --git a/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/unit/RouterTest.kt b/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/unit/RouterTest.kt index 2f835af..68c356e 100644 --- a/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/unit/RouterTest.kt +++ b/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/unit/RouterTest.kt @@ -40,7 +40,7 @@ class RouterTest companion object { private lateinit var router: MockRouter - private val channel = Channel() + private val channel = Channel() private val controller = NotAsyncController::class.java @BeforeClass @JvmStatic From 7630c9f32121e8277f574f002621825f5b43432a Mon Sep 17 00:00:00 2001 From: Dragas Date: Sun, 3 Sep 2017 10:28:01 +0300 Subject: [PATCH 5/8] tests and async implementation --- async/build.gradle | 2 +- .../chatty/v3/async/route/AsyncRoute.kt | 40 +++++++++++++---- .../async/mock/controller/MockController.kt | 13 +++--- .../mock/controller/NotAsyncController.kt | 7 +-- .../chatty/v3/async/mock/io/MockAdapter.kt | 7 +-- .../chatty/v3/async/mock/io/MockEndpoint.kt | 5 ++- .../chatty/v3/async/mock/main/MockClient.kt | 12 +++--- .../v3/async/mock/message/MockRequest.kt | 9 ++++ .../chatty/v3/async/mock/route/MockRoute.kt | 13 +++--- .../chatty/v3/async/mock/route/MockRouter.kt | 3 +- .../dragas/chatty/v3/async/unit/ClientTest.kt | 43 +++++++++++++++++++ .../chatty/v3/async/unit/ControllerTest.kt | 3 +- .../dragas/chatty/v3/async/unit/RouteTest.kt | 9 ++-- .../dragas/chatty/v3/async/unit/RouterTest.kt | 4 +- 14 files changed, 126 insertions(+), 44 deletions(-) create mode 100644 async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/message/MockRequest.kt create mode 100644 async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/unit/ClientTest.kt diff --git a/async/build.gradle b/async/build.gradle index 4698e1a..3adb651 100644 --- a/async/build.gradle +++ b/async/build.gradle @@ -38,4 +38,4 @@ compileTestKotlin { kotlinOptions.jvmTarget = "1.8" } -apply from: "../upload.gradle" \ No newline at end of file +//apply from: "../upload.gradle" \ No newline at end of file diff --git a/async/src/main/kotlin/lt/saltyjuice/dragas/chatty/v3/async/route/AsyncRoute.kt b/async/src/main/kotlin/lt/saltyjuice/dragas/chatty/v3/async/route/AsyncRoute.kt index b9f2870..978b46d 100644 --- a/async/src/main/kotlin/lt/saltyjuice/dragas/chatty/v3/async/route/AsyncRoute.kt +++ b/async/src/main/kotlin/lt/saltyjuice/dragas/chatty/v3/async/route/AsyncRoute.kt @@ -3,7 +3,9 @@ package lt.saltyjuice.dragas.chatty.v3.async.route import kotlinx.coroutines.experimental.CommonPool import kotlinx.coroutines.experimental.channels.SendChannel import kotlinx.coroutines.experimental.channels.actor +import kotlinx.coroutines.experimental.channels.consumeEach import kotlinx.coroutines.experimental.launch +import lt.saltyjuice.dragas.chatty.v3.core.exception.RouteBuilderException import lt.saltyjuice.dragas.chatty.v3.core.middleware.AfterMiddleware import lt.saltyjuice.dragas.chatty.v3.core.middleware.BeforeMiddleware import lt.saltyjuice.dragas.chatty.v3.core.route.Controller @@ -18,16 +20,29 @@ import java.lang.reflect.Method */ open class AsyncRoute : Route() { + /** + * Provided by client while building the routes. This is how client listens to any responses generated by routes. + */ protected open lateinit var responseChannel: SendChannel + /** + * + */ protected open val listener = actor(CommonPool) { - for (response in channel) - this@AsyncRoute.attemptRespond(response) + channel.consumeEach { this@AsyncRoute.attemptRespondSuspend(it) } + } + + override fun getCurrentControllerInstance(): AsyncController + { + return super.getCurrentControllerInstance() as AsyncController } - override fun getControllerInstance(): Controller + override fun getControllerInstance(): AsyncController { - return super.getControllerInstance().apply { if (this is AsyncController) listen(listener) } + val controller = super.getControllerInstance() as AsyncController + controller.listen(listener) + return controller + //return super.getControllerInstance().apply { if (this is AsyncController) listen(listener) } } /** @@ -35,10 +50,15 @@ open class AsyncRoute : Route() */ override fun attemptRespond(response: Response) { - launch(CommonPool) - { - if (canRespond(response)) responseChannel.send(response) - } + launch(CommonPool) { attemptRespondSuspend(response) } + } + + /** + * Suspendable version of [attemptRespond] + */ + open suspend fun attemptRespondSuspend(response: Response) + { + if (canRespond(response)) responseChannel.send(response) } /** @@ -87,11 +107,15 @@ open class AsyncRoute : Route() override fun consume(controller: Class>, method: Method): AsyncRoute.Builder { + if (!AsyncController::class.java.isAssignableFrom(controller)) + throw RouteBuilderException("Async routes need to use async controllers.") return super.consume(controller, method) as AsyncRoute.Builder } override fun controller(clazz: Class>): AsyncRoute.Builder { + if (!AsyncController::class.java.isAssignableFrom(clazz)) + throw RouteBuilderException("Async routes need to use async controllers.") return super.controller(clazz) as AsyncRoute.Builder } diff --git a/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/controller/MockController.kt b/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/controller/MockController.kt index 3a179e7..28d4844 100644 --- a/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/controller/MockController.kt +++ b/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/controller/MockController.kt @@ -1,31 +1,32 @@ package lt.saltyjuice.dragas.chatty.v3.async.mock.controller +import lt.saltyjuice.dragas.chatty.v3.async.mock.message.MockRequest import lt.saltyjuice.dragas.chatty.v3.async.route.AsyncController import lt.saltyjuice.dragas.chatty.v3.core.route.On import lt.saltyjuice.dragas.chatty.v3.core.route.When class MockController : AsyncController() { - @On(Int::class) + @On(MockRequest::class) @When("isEven") - fun provideModuloOfTwoWritesLater(request: Int) + fun provideModuloOfTwoWritesLater(request: MockRequest) { writeResponse((request % 2).toFloat()) } - fun isEven(request: Int): Boolean + fun isEven(request: MockRequest): Boolean { return request % 2 == 0 } - @On(Int::class) + @On(MockRequest::class) @When("isOdd") - fun providesModuloOfTwoWritesNow(request: Int) + fun providesModuloOfTwoWritesNow(request: MockRequest) { writeResponse((request % 2).toFloat(), true) } - fun isOdd(request: Int): Boolean + fun isOdd(request: MockRequest): Boolean { return request % 2 != 0 } diff --git a/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/controller/NotAsyncController.kt b/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/controller/NotAsyncController.kt index 0d52daf..2584713 100644 --- a/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/controller/NotAsyncController.kt +++ b/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/controller/NotAsyncController.kt @@ -1,5 +1,6 @@ package lt.saltyjuice.dragas.chatty.v3.async.mock.controller +import lt.saltyjuice.dragas.chatty.v3.async.mock.message.MockRequest import lt.saltyjuice.dragas.chatty.v3.core.route.Controller import lt.saltyjuice.dragas.chatty.v3.core.route.On import lt.saltyjuice.dragas.chatty.v3.core.route.When @@ -8,12 +9,12 @@ class NotAsyncController : Controller() { @On(Int::class) @When("isEven") - fun onEven(request: Int) + fun onEven(request: MockRequest) { - writeResponse(request.toFloat()) + writeResponse(request.value.toFloat()) } - fun isEven(request: Int): Boolean + fun isEven(request: MockRequest): Boolean { return request % 2 == 0 } diff --git a/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/io/MockAdapter.kt b/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/io/MockAdapter.kt index afea2a9..0e4e62e 100644 --- a/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/io/MockAdapter.kt +++ b/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/io/MockAdapter.kt @@ -1,17 +1,18 @@ package lt.saltyjuice.dragas.chatty.v3.async.mock.io +import lt.saltyjuice.dragas.chatty.v3.async.mock.message.MockRequest import lt.saltyjuice.dragas.chatty.v3.core.adapter.Deserializer import lt.saltyjuice.dragas.chatty.v3.core.adapter.Serializer -class MockAdapter : Serializer, Deserializer +class MockAdapter : Serializer, Deserializer { override fun serialize(any: Float): Float { return any } - override fun deserialize(block: Int): Int + override fun deserialize(block: Int): MockRequest { - return block + return MockRequest(block) } } \ No newline at end of file diff --git a/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/io/MockEndpoint.kt b/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/io/MockEndpoint.kt index 9a0ca14..c5d2f6b 100644 --- a/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/io/MockEndpoint.kt +++ b/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/io/MockEndpoint.kt @@ -2,14 +2,15 @@ package lt.saltyjuice.dragas.chatty.v3.async.mock.io import kotlinx.coroutines.experimental.channels.Channel import kotlinx.coroutines.experimental.runBlocking +import lt.saltyjuice.dragas.chatty.v3.async.mock.message.MockRequest import lt.saltyjuice.dragas.chatty.v3.core.io.Input import lt.saltyjuice.dragas.chatty.v3.core.io.Output -class MockEndpoint(private val requestChannel: Channel, private val responseChannel: Channel) : Input, Output +class MockEndpoint(private val requestChannel: Channel, private val responseChannel: Channel) : Input, Output { override val adapter: MockAdapter = MockAdapter() - override fun getRequest(): Int = runBlocking() + override fun getRequest(): MockRequest = runBlocking() { requestChannel.receive() } diff --git a/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/main/MockClient.kt b/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/main/MockClient.kt index d3fd086..b5c404d 100644 --- a/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/main/MockClient.kt +++ b/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/main/MockClient.kt @@ -3,23 +3,21 @@ package lt.saltyjuice.dragas.chatty.v3.async.mock.main import lt.saltyjuice.dragas.chatty.v3.async.main.AsyncClient import lt.saltyjuice.dragas.chatty.v3.async.mock.controller.MockController import lt.saltyjuice.dragas.chatty.v3.async.mock.io.MockEndpoint +import lt.saltyjuice.dragas.chatty.v3.async.mock.message.MockRequest import lt.saltyjuice.dragas.chatty.v3.async.mock.route.MockRouter import lt.saltyjuice.dragas.chatty.v3.core.route.UsesControllers @UsesControllers(MockController::class) -class MockClient(endpoint: MockEndpoint) : AsyncClient() +class MockClient(endpoint: MockEndpoint) : AsyncClient() { override val sout: MockEndpoint = endpoint override val router: MockRouter = MockRouter(responseChannel) override val sin: MockEndpoint = endpoint - /*override fun onConnect() + override fun onConnect() { - launch(CommonPool) - { - writeResponse(2f) - } - }*/ + super.onConnect() + } /*override fun writeResponse(response: Float) { diff --git a/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/message/MockRequest.kt b/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/message/MockRequest.kt new file mode 100644 index 0000000..c213622 --- /dev/null +++ b/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/message/MockRequest.kt @@ -0,0 +1,9 @@ +package lt.saltyjuice.dragas.chatty.v3.async.mock.message + +class MockRequest(val value: Int) +{ + operator fun rem(int: Int): Int + { + return value % int + } +} \ No newline at end of file diff --git a/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/route/MockRoute.kt b/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/route/MockRoute.kt index 5066a19..9636e41 100644 --- a/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/route/MockRoute.kt +++ b/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/route/MockRoute.kt @@ -1,6 +1,7 @@ package lt.saltyjuice.dragas.chatty.v3.async.mock.route import kotlinx.coroutines.experimental.channels.SendChannel +import lt.saltyjuice.dragas.chatty.v3.async.mock.message.MockRequest import lt.saltyjuice.dragas.chatty.v3.async.route.AsyncRoute import lt.saltyjuice.dragas.chatty.v3.core.middleware.AfterMiddleware import lt.saltyjuice.dragas.chatty.v3.core.middleware.BeforeMiddleware @@ -8,16 +9,16 @@ import lt.saltyjuice.dragas.chatty.v3.core.route.Controller import lt.saltyjuice.dragas.chatty.v3.core.route.Route import java.lang.reflect.Method -open class MockRoute : AsyncRoute() +open class MockRoute : AsyncRoute() { - open class Builder : AsyncRoute.Builder() + open class Builder : AsyncRoute.Builder() { override fun returnableRoute(): MockRoute { return MockRoute() } - override fun adapt(route: Route): MockRoute + override fun adapt(route: Route): MockRoute { return super.adapt(route) as MockRoute } @@ -27,12 +28,12 @@ open class MockRoute : AsyncRoute() return super.after(clazz) as Builder } - override fun before(clazz: Class>): Builder + override fun before(clazz: Class>): Builder { return super.before(clazz) as Builder } - override fun callback(callback: (Route, Int) -> Unit): Builder + override fun callback(callback: (Route, MockRequest) -> Unit): Builder { return super.callback(callback) as Builder } @@ -52,7 +53,7 @@ open class MockRoute : AsyncRoute() return super.description(string) as Builder } - override fun testCallback(callback: (Route, Int) -> Boolean): Builder + override fun testCallback(callback: (Route, MockRequest) -> Boolean): Builder { return super.testCallback(callback) as Builder } diff --git a/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/route/MockRouter.kt b/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/route/MockRouter.kt index bbe58ca..d395548 100644 --- a/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/route/MockRouter.kt +++ b/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/mock/route/MockRouter.kt @@ -1,9 +1,10 @@ package lt.saltyjuice.dragas.chatty.v3.async.mock.route import kotlinx.coroutines.experimental.channels.SendChannel +import lt.saltyjuice.dragas.chatty.v3.async.mock.message.MockRequest import lt.saltyjuice.dragas.chatty.v3.async.route.AsyncRouter -class MockRouter(channel: SendChannel) : AsyncRouter(channel) +class MockRouter(channel: SendChannel) : AsyncRouter(channel) { override fun builder(): MockRoute.Builder { diff --git a/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/unit/ClientTest.kt b/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/unit/ClientTest.kt new file mode 100644 index 0000000..3b84f44 --- /dev/null +++ b/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/unit/ClientTest.kt @@ -0,0 +1,43 @@ +package lt.saltyjuice.dragas.chatty.v3.async.unit + +import kotlinx.coroutines.experimental.channels.Channel +import kotlinx.coroutines.experimental.runBlocking +import lt.saltyjuice.dragas.chatty.v3.async.mock.io.MockEndpoint +import lt.saltyjuice.dragas.chatty.v3.async.mock.main.MockClient +import lt.saltyjuice.dragas.chatty.v3.async.mock.message.MockRequest +import org.junit.Assert +import org.junit.BeforeClass +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +@RunWith(JUnit4::class) +class ClientTest +{ + /*@Rule + @JvmField + val timeout = Timeout(1000, TimeUnit.MILLISECONDS)*/ + + @Test + fun clientIsCapableOfConsumeAndResponding() = runBlocking() + { + requestChannel.send(MockRequest(5)) + client.run() + Assert.assertEquals(1.0f, responseChannel.receive()) + } + + companion object + { + private lateinit var client: MockClient + private val responseChannel: Channel = Channel(1) + private val requestChannel: Channel = Channel(1) + @BeforeClass + @JvmStatic + fun init() + { + client = MockClient(MockEndpoint(requestChannel, responseChannel)) + client.initialize() + client.onConnect() + } + } +} \ No newline at end of file diff --git a/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/unit/ControllerTest.kt b/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/unit/ControllerTest.kt index 7eb98c4..4b05d43 100644 --- a/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/unit/ControllerTest.kt +++ b/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/unit/ControllerTest.kt @@ -3,6 +3,7 @@ package lt.saltyjuice.dragas.chatty.v3.async.unit import kotlinx.coroutines.experimental.channels.Channel import kotlinx.coroutines.experimental.runBlocking import lt.saltyjuice.dragas.chatty.v3.async.mock.controller.MockController +import lt.saltyjuice.dragas.chatty.v3.async.mock.message.MockRequest import org.junit.* import org.junit.rules.Timeout import org.junit.runner.RunWith @@ -19,7 +20,7 @@ open class ControllerTest @Test fun controllerWritesResponseNow() = runBlocking() { - controller.providesModuloOfTwoWritesNow(2) + controller.providesModuloOfTwoWritesNow(MockRequest(2)) Assert.assertEquals(0f, channel.receive()) } diff --git a/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/unit/RouteTest.kt b/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/unit/RouteTest.kt index 900164f..21002b6 100644 --- a/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/unit/RouteTest.kt +++ b/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/unit/RouteTest.kt @@ -3,6 +3,7 @@ package lt.saltyjuice.dragas.chatty.v3.async.unit import kotlinx.coroutines.experimental.channels.Channel import kotlinx.coroutines.experimental.runBlocking import lt.saltyjuice.dragas.chatty.v3.async.mock.controller.MockController +import lt.saltyjuice.dragas.chatty.v3.async.mock.message.MockRequest import lt.saltyjuice.dragas.chatty.v3.async.mock.route.MockRoute import org.junit.AfterClass import org.junit.Assert @@ -23,15 +24,15 @@ class RouteTest @Test fun routeRespondsAsynchronously() = runBlocking() { - buildAsyncRoute().attemptTrigger(3) - Assert.assertEquals(1, channel.receive()) + buildAsyncRoute().attemptTrigger(MockRequest(3)) + Assert.assertEquals(1.0f, channel.receive()) } @Test fun routeRespondsSynchonously() = runBlocking() { - buildSyncRoute().attemptTrigger(2) - Assert.assertEquals(0, channel.receive()) + buildSyncRoute().attemptTrigger(MockRequest(2)) + Assert.assertEquals(0.0f, channel.receive()) } companion object diff --git a/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/unit/RouterTest.kt b/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/unit/RouterTest.kt index 68c356e..ca5ec48 100644 --- a/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/unit/RouterTest.kt +++ b/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/unit/RouterTest.kt @@ -18,7 +18,7 @@ class RouterTest { shouldThrow(RouteBuilderException::class.java) { - router.consume(NotAsyncController::class.java) + router.consume(controller) } } @@ -31,7 +31,7 @@ class RouterTest } catch (err: Throwable) { - clazz.forEach { if (it.isAssignableFrom(err.javaClass)) threw = true } + clazz.forEach { if (it.isAssignableFrom(err.javaClass)) threw = true else throw err } } Assert.assertTrue(threw) From d811fd33ef92b049e647b5a5f7450198ce2b62d0 Mon Sep 17 00:00:00 2001 From: Dragas Date: Sun, 3 Sep 2017 10:33:11 +0300 Subject: [PATCH 6/8] tests and async implementation --- .../saltyjuice/dragas/chatty/v3/async/unit/ClientTest.kt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/unit/ClientTest.kt b/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/unit/ClientTest.kt index 3b84f44..e34575b 100644 --- a/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/unit/ClientTest.kt +++ b/async/src/test/kotlin/lt/saltyjuice/dragas/chatty/v3/async/unit/ClientTest.kt @@ -7,16 +7,19 @@ import lt.saltyjuice.dragas.chatty.v3.async.mock.main.MockClient import lt.saltyjuice.dragas.chatty.v3.async.mock.message.MockRequest import org.junit.Assert import org.junit.BeforeClass +import org.junit.Rule import org.junit.Test +import org.junit.rules.Timeout import org.junit.runner.RunWith import org.junit.runners.JUnit4 +import java.util.concurrent.TimeUnit @RunWith(JUnit4::class) class ClientTest { - /*@Rule + @Rule @JvmField - val timeout = Timeout(1000, TimeUnit.MILLISECONDS)*/ + val timeout = Timeout(1000, TimeUnit.MILLISECONDS) @Test fun clientIsCapableOfConsumeAndResponding() = runBlocking() From 3368f7b030cb3b9deb2fd7cb8496d1f98a00b7c2 Mon Sep 17 00:00:00 2001 From: Dragas Date: Mon, 4 Sep 2017 23:31:19 +0300 Subject: [PATCH 7/8] depends on 3.0.1 --- async/build.gradle | 4 ++-- settings.gradle | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/async/build.gradle b/async/build.gradle index 3adb651..906b052 100644 --- a/async/build.gradle +++ b/async/build.gradle @@ -1,5 +1,5 @@ group 'lt.saltyjuice.dragas' -version '3.0.0' +version '3.0.1' buildscript { ext.kotlin_version = '1.1.4-2' @@ -26,7 +26,7 @@ repositories { dependencies { compile "org.jetbrains.kotlin:kotlin-stdlib-jre8:$kotlin_version" - compile "lt.saltyjuice.dragas:chatty-core:3.0.0" + compile "lt.saltyjuice.dragas:chatty-core:3.0.1" compile 'org.jetbrains.kotlinx:kotlinx-coroutines-core:0.18' testCompile group: 'junit', name: 'junit', version: '4.12' } diff --git a/settings.gradle b/settings.gradle index 8e741aa..67108e7 100644 --- a/settings.gradle +++ b/settings.gradle @@ -5,5 +5,5 @@ include ':birc' include ":websocket" include ':discord' include ':biscord' -include 'async' +include ':async' From 70834c7ea7fa1c2b9ec03432423defb774f4f885 Mon Sep 17 00:00:00 2001 From: Dragas Date: Tue, 5 Sep 2017 09:47:07 +0300 Subject: [PATCH 8/8] removed redundant envs --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 1521776..6985902 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,5 +13,4 @@ script: after_success: - "chmod a+x automerger" - - "BRANCHES_TO_MERGE_REGEX='-dev$' BRANCH_TO_MERGE_INTO=master GITHUB_REPO=Dragas/bIRC" - ./automerger \ No newline at end of file