Testing suspension functions in AssertJ? - kotlin

I'm in the process of converting my JUnit tests to AssertJ, but I'm running into some issues with AssertJ.
I'm not quite sure how to test a suspend function using AssertJ.
In JUnit, I can do this.
runTest {
assertThrows<Exception> {
//suspend function
messagePublisher.publish(//message)
}
}
However, in assertJ:
runTest {
assertThatExceptionOfType(Exception::class.java).isThrownBy {
// the same suspend function
messagePublisher.publish(message)
}
}
I get the error:
Suspension functions can be called only within coroutine body`
Is there a way to easily test suspension functions using assertJ? How can I use a coroutine body here?

Related

How to cancel kotlin coroutine with potentially "un-cancellable" method call inside it?

I have this piece of code:
// this method is used to evaluate the input string, and it returns evaluation result in string format
fun process(input: String): String {
val timeoutMillis = 5000L
val page = browser.newPage()
try {
val result = runBlocking {
withTimeout(timeoutMillis) {
val result = page.evaluate(input).toString()
return#withTimeout result
}
}
return result
} catch (playwrightException: PlaywrightException) {
return "Could not parse template! '${playwrightException.localizedMessage}'"
} catch (timeoutException: TimeoutCancellationException) {
return "Could not parse template! (timeout)"
} finally {
page.close()
}
}
It should throw exception after 5 seconds if the method is taking too long to execute (example: input potentially contains infinite loop) but it doesent (becomes deadlock I assume) coz coroutines should be cooperative. But the method I am calling is from another library and I have no control over its computation (for sticking yield() or smth like it).
So the question is: is it even possible to timeout such coroutine? if yes, then how?
Should I use java thread insted and just kill it after some time?
But the method I am calling is from another library and I have no control over its computation (for sticking yield() or smth like it).
If that is the case, I see mainly 2 situations here:
the library is aware that this is a long-running operation and supports thread interrupts to cancel it. This is the case for Thread.sleep and some I/O operations.
the library function really does block the calling thread for the whole time of the operation, and wasn't designed to handle thread interrupts
Situation 1: the library function is interruptible
If you are lucky enough to be in situation 1, then simply wrap the library's call into a runInterruptible block, and the coroutines library will translate cancellation into thread interruptions:
fun main() {
runBlocking {
val elapsed = measureTimeMillis {
withTimeoutOrNull(100.milliseconds) {
runInterruptible {
interruptibleBlockingCall()
}
}
}
println("Done in ${elapsed}ms")
}
}
private fun interruptibleBlockingCall() {
Thread.sleep(3000)
}
Situation 2: the library function is NOT interruptible
In the more likely situation 2, you're kind of out of luck.
Should I use java thread insted and just kill it after some time?
There is no such thing as "killing a thread" in Java. See Why is Thread.stop deprecated?, or How do you kill a Thread in Java?.
In short, in that case you do not have a choice but to block some thread.
I do not know a solution to this problem that doesn't leak resources. Using an ExecutorService would not help if the task doesn't support thread interrupts - the threads will not die even with shutdownNow() (which uses interrupts).
Of course, the blocked thread doesn't have to be your thread. You can technically launch a separate coroutine on another thread (using another dispatcher if yours is single-threaded), to wrap the libary function call, and then join() the job inside a withTimeout to avoid waiting for it forever. That is however probably bad, because you're basically deferring the problem to whichever scope you use to launch the uncancellable task (this is actually why we can't use a simple withContext here).
If you use GlobalScope or another long-running scope, you effectively leak the hanging coroutine (without knowing for how long).
If you use a more local parent scope, you defer the problem to that scope. This is for instance the case if you use the scope of an enclosing runBlocking (like in your example), which makes this solution pointless:
fun main() {
val elapsed = measureTimeMillis {
doStuff()
}
println("Completely done in ${elapsed}ms")
}
private fun doStuff() {
runBlocking {
val nonCancellableJob = launch(Dispatchers.IO) {
uncancellableBlockingCall()
}
val elapsed = measureTimeMillis {
withTimeoutOrNull(100.milliseconds) {
nonCancellableJob.join()
}
}
println("Done waiting in ${elapsed}ms")
} // /!\ runBlocking will still wait here for the uncancellable child coroutine
}
// Thread.sleep is in fact interruptible but let's assume it's not for the sake of the example
private fun uncancellableBlockingCall() {
Thread.sleep(3000)
}
Outputs something like:
Done waiting in 122ms
Completely done in 3055ms
So the bottom line is either live with this long thing potentially hanging, or ask the developers of that library to handle interruption or make the task cancellable.

callback function called from coroutine does not run with KotlinJS

I am trying to write a Kotlin function that executes a HTTP request, then gives the result back to JavaScript.
Because with the IR compiler I cannot use a suspended function from JavaScript, I am trying to use a callback instead.
However, the callback is never executed when called from a coroutine.
Here's a small sample of what I am doing:
private val _httpClient = HttpClient(JsClient()) {
install(ContentNegotiation) { json() }
defaultRequest { url(settings.baseUrl) }
}
fun requestJwtVcJsonCredential(
request: JSJwtVcJsonVerifiableCredentialRequest,
callback: (JSDeferredJsonCredentialResponse?, JSJwtVcJsonVerifiableCredentialResponse?, Any?) -> Unit
) {
CoroutineScope(_httpClient.coroutineContext).launch {
// call suspend function
val response = requestCredential(convert(request))
// this never runs, even though the coroutine does run successfully
println("Coroutine received: $response")
callback(response.first, response.second, response.third)
}
}
I've noticed this question had a similar problem in Android, but the suggested fix does not apply to JavaScript... specifically, using a Channel does not help in my case because I don't have a coroutine to receive from, and trying to start a new coroutine to receive from the channel, then calling the callback from that coroutine, also doesn't work (the root problem seems to be that I cannot call a callback function from any coroutine).
What's the best way to solve this problem? Assume the function I need to call is a suspend function (the HTTP Client function) and I cannot change that, but I could change everything around it so that it works from a non-suspend function (as that's a limitation of Kotlin JS).
The root problem was that the suspend function was actually failing, but there seems to be no default exception handler so the Exception was not logged anywhere, causing the function to fail silently, making it look like the callback was being called but not executing.
However, I think it's worth it mentioning that KotlinJS supports Promise<T>, so the better way to expose a suspend function to JS is to actually write an "adapter" function that returns a Promise instead.
There is a promise extension function on CouroutineScope which can be used for this.
So, for example, if you've got a Kotlin function like this:
suspend fun makeRequest(request: Request): Response
To expose it in JavaScript you can have an adapter function like this:
#JsExport
fun makeRequestJS(request: Request): Promise<Response> {
// KTor's HttpClient itself is a CoroutineScope
return _httpClient.promise { makeRequest(request) }
}
This avoids the need to introduce a callback function.

Difference between GlobalScope and runBlocking when waiting for multiple async

I have a Kotlin Backend/server API using Ktor, and inside a certain endpoint's service logic I need to concurrently get details for a list of ids and then return it all to the client with the 200 response.
The way I wanted to do it is by using async{} and awaitAll()
However, I can't understand whether I should use runBlocking or GlobalScope.
What is really the difference here?
fun getDetails(): List<Detail> {
val fetched: MutableList<Details> = mutableListOf()
GlobalScope.launch { --> Option 1
runBlocking { ---> Option 2
Dispatchers.IO --> Option 3 (or any other dispatcher ..)
myIds.map { id ->
async {
val providerDetails = getDetails(id)
fetched += providerDetails
}
}.awaitAll()
}
return fetched
}
launch starts a coroutine that runs in parallel with your current code, so fetched would still be empty by the time your getDetails() function returns. The coroutine will continue running and mutating the List that you have passed out of the function while the code that retrieved the list already has the reference back and will be using it, so there's a pretty good chance of triggering a ConcurrentModificationException. Basically, this is not a viable solution at all.
runBlocking runs a coroutine while blocking the thread that called it. The coroutine will be completely finished before the return fetched line, so this will work if you are OK with blocking the calling thread.
Specifying a Dispatcher isn't an alternative to launch or runBlocking. It is an argument that you can add to either to determine the thread pool used for the coroutine and its children. Since you are doing IO and parallel work, you should probably be using runBlocking(Dispatchers.IO).
Your code can be simplified to avoid the extra, unnecessary mutable list:
fun getDetails(): List<Detail> = runBlocking(Dispatchers.IO) {
myIds.map { id ->
async {
getDetails(id)
}
}.awaitAll()
}
Note that this function will rethrow any exceptions thrown by getDetails().
If your project uses coroutines more generally, you probably have higher level coroutines running, in which case this should probably be a suspend function (non-blocking) instead:
suspend fun getDetails(): List<Detail> = withContext(Dispatchers.IO) {
myIds.map { id ->
async {
getDetails(id)
}
}.awaitAll()
}

Stop infinite function in kotlin using coroutines - difference between async and GlobalScope.async

I need to wrap some Java-callback function using timeout. Callback may be never called, so it should be interrupted with exception. Here was my first try:
fun main() = runBlocking {
withTimeout(500) {
async {
notCalledCallback()
}.await()
}
Unit
}
private suspend fun notCalledCallback() = suspendCoroutine<Boolean> { cont ->
startScanning(object : SomeCallback {
override fun done() {
cont.resume(true)
}
})
}
fun startScanning(callBack: SomeCallback) {
// callback may never be invoked
// callBack.done()
}
interface SomeCallback {
fun done()
}
I expected to have a TimeoutCancellationException after 500ms, but actually it never happens. However if I replace
async {
notCalledCallback()
}.await()
with
GlobalScope.async {
notCalledCallback()
}.await()
it starts to work. Why? What is the difference between async and GlobalScope.async in this case and why it works in latter case?
while (true) {
Thread.sleep(1)
}
This block of code does not comply with coroutine practices and doesn't offer the coroutine framework any opportunity to cancel it.
A correct implementation of infinityFunction() would be to simply call awaitCancellation. Alternately, you could replace Thread.sleep with delay.
Notably, using GlobalScope actually breaks the correct relationship between your coroutines (making the async block not a child of the calling coroutine), with the result that your main function doesn't wait for infinityFunction() to properly finish cancelling. While this appears to make your code work, it actually just conceals a worse bug.
The answer is actually very simple: suspendCoroutine() is not cancellable. You need to instead use a very similar function: suspendCancellableCoroutine().
Please be aware that ideally you should not only swap one function with another, but also properly cancel the asynchronous operation before resuming the coroutine. Otherwise you leak this background operation as it is entirely detached from your execution context. You can detect cancellations with cont.invokeOnCancellation(), as described in the documentation linked above.
If you use GlobalScope then you await() for the operation in your current execution context, but the operation itself runs in another context. In this case if you cancel, then you cancel waiting, but you don't cancel the operation and you don't care whether it completes or not.

Use Kotlin coroutines to make multiple webservice calls simultaneously

I tried reading the docs, but it just doesn't make sense to me.
I need to make three calls to an external webserivce and log the result of each after it returns. Each webservice call is indepdentant of the responses of the others. Done synchronously, it looks like this:
fun makeWebserviceCalls(){
callOne()
callTwo()
callThree()
}
fun callOne(){
// make webservice call
// log result
}
fun callTwo(){
// make webservice call
// log result
}
fun callThree(){
// make webservice call
// log result
}
Now I just need to do that in parallel. It shouldn't be that hard, but it's just not making sense to me.
I've tried:
fun makeWebserviceCalls(){
callOne()
callTwo()
callThree()
}
fun callOne(){
launch{
// make webservice call
// log result
}
}
but that doesn't compile.
I've tried:
fun makeWebserviceCalls(){
runBlocking{
callOne()
callTwo()
callThree()
}
}
suspend fun callOne(){
launch{
// make webservice call
// log result
}
}
but that doesn't compile.
I've tried:
fun makeWebserviceCalls(){
runBlocking{
callOne()
callTwo()
callThree()
}
}
suspend fun callOne(){
withContext(Dispatchers.IO){
// make webservice call
// log result
}
}
but this can't be right, because withContext is used when you need a result returned, which I don't.
What's the right way to do what I'm trying to do?
The primary goal of Coroutines is to do efficient asynchronous operations, which is different than doing concurrent operations. Here’s an example from the official docs:
val client = HttpClient()
//Running in the main thread, start a `get` call
client.get<String>("https://example.com/some/rest/call")
//The get call will suspend and let other work happen in the main thread, and resume when the get call completes
All of the above happen on the main thread, no separate threads are spawned. A future versions of Coroutines will provide OOTB support for concurrent Coroutines on multiple threads, but the main branch has no support for that yet.
A suspend function can be executed on a different thread:
suspend fun differentThread() = withContext(Dispatchers.Default){
println("Different thread")
}
Of course, if you call the suspend function from a regular function, you’ll need to do it within runBlocking.
Other alternatives are proposed here: https://kotlinlang.org/docs/mobile/concurrency-and-coroutines.html#alternatives-to-kotlinx-coroutines. For simple use cases, CoroutineWorker is a good option.
For more details, see the official docs: https://kotlinlang.org/docs/mobile/concurrency-and-coroutines.html
If you need your makeWebserviceCalls() to wait until all requests to finish:
runBlocking(Dispatchers.IO) {
launch { callOne() }
launch { callTwo() }
launch { callThree() }
}
If you need to start them in the background and return immediately:
GlobalScope.launch(Dispatchers.IO) {
launch { callOne() }
launch { callTwo() }
launch { callThree() }
}
But you need to understand that this is not the usual way of using coroutines. Normally, your makeWebserviceCalls() function and all call* functions would be suspend functions which makes them more coroutines-friendly.
You start parallel execution from out of the coroutines context, also parallel blocks of code are blocking and in such a case I'm not sure if it makes sense to use coroutines at all. You can just start 3 background threads, it will be effectively almost the same.