https://kotlinlang.org logo
Title
k

Kris Wong

02/05/2021, 4:37 PM
when calling a 
suspend
 function within 
runBlocking
 , the following code should not be executed until the function completes, correct? I am seeing some flakiness in a test that doesn't make sense to me.
✔️ 1
w

wasyl

02/05/2021, 4:38 PM
Can you paste the function?
runBlocking
doesn’t change all suspend functions into blocking ones
k

Kris Wong

02/05/2021, 4:39 PM
the test function or the suspending function?
w

wasyl

02/05/2021, 4:39 PM
You can still run things in parallel for example
the test function or the suspending function?
The test function
k

Kris Wong

02/05/2021, 4:40 PM
@Test fun someTest() = runBlocking {
  someSuspendingFunction()
  assertTrue(shouldBeTrue) // expects above function to have returned
}
it's working most of the time
l

louiscad

02/05/2021, 4:43 PM
@Kris Wong Which platform? Can you reproduce it with any code, or only an expectation is missed for a particular function you have written?
k

Kris Wong

02/05/2021, 4:46 PM
i am currently seeing a failure running junit tests from the command line via
./gradlew testDebugUnitTest
. it's a common test and passes on iOS, and also when run in the IDE.
l

louiscad

02/05/2021, 4:46 PM
"Failure" is too broad, you know it.
k

Kris Wong

02/05/2021, 4:47 PM
it's only 1 test that is failing. other tests are exercising the same code in similar ways. the suspending function ends up calling Ktor
HttpClient().post
l

louiscad

02/05/2021, 4:48 PM
Define "failing", please.
k

Kris Wong

02/05/2021, 4:48 PM
a boolean gets set to true in a callback that is called from the suspending function
which is not happening. the callback is not called in this one case.
w

wasyl

02/05/2021, 4:49 PM
My guess is that
someSuspendingFunction
does thing asynchronously, launches a coroutine in a separate scope for example. And the test is simply flaky, sometimes the asynchronous thing manages to complete and sometimes it doesn’t
k

Kris Wong

02/05/2021, 4:49 PM
also my guess, just not what I expected, and I'm not quite sure how to fix it
unfortunately I am limited to APIs in stdlib-common
w

wasyl

02/05/2021, 4:51 PM
I’m not quite sure how to fix it
It really depends on what
someSuspendingFunction()
does
l

louiscad

02/05/2021, 4:51 PM
You said it works all the time on iOS and in the IDE. If so, then on which platform is it sometimes failing?
k

Kris Wong

02/05/2021, 4:51 PM
android junit tests
which is of course just JVM
l

louiscad

02/05/2021, 4:53 PM
AGP 7?
Running in the JVM or in an emulator or device? Because Android is not "just JVM" despite it being almost it.
k

Kris Wong

02/05/2021, 4:55 PM
android unit tests run in the JVM (unlike connected tests). AGP 4.1.2.
l

louiscad

02/05/2021, 4:55 PM
Alright. Weird then :blob-thinking-upside-down:
k

Kris Wong

02/05/2021, 4:56 PM
I believe something like
withContext
might do the trick, but I don't think that API is available
l

louiscad

02/05/2021, 4:57 PM
It is
You can also use
Dispatchers.Default { }
, or just
coroutineScope { }
if you fear you broke structured concurrency in a way or another at the immediate level.
If you have a class implementing
CoroutineScope
, it's very likely you're breaking structured concurrency BTW.
k

Kris Wong

02/05/2021, 4:59 PM
the only scope my code is creating is provided by
runBlocking
, however I am sure Ktor is creating one or more scopes
withContext
didn't fix it
l

louiscad

02/05/2021, 4:59 PM
Then we cannot help without a reproducer
It might be a ktor specific issue
k

Kris Wong

02/05/2021, 5:14 PM
oh snap, I notice the test execution order is not the same between the IDE and the command line. I think I may be on to something with that.
l

louiscad

02/05/2021, 5:15 PM
Shared mutable state? 🤔
k

Kris Wong

02/05/2021, 5:17 PM
yes, it succeeds when isolate the failing test
nothing to see here, sorry for the trouble!
I have tracked this down to what appears to be a very strange IllegalMonitorStateException with ReentrantLock that was being swallowed
man, yet another red herring. the root of the problem was that I was passing too small of a grace period/timeout to stop the Ktor server I was using for the tests.
moral of the story,
runBlocking
is working as expected
l

louiscad

02/05/2021, 10:04 PM
Why don't you stop the server only when all the tests are done?
g

gildor

02/08/2021, 3:07 AM
Why start/stop server on every test, otherwise it breaks test isolation
k

Kris Wong

02/08/2021, 2:02 PM
test isolation is exactly why i start and stop it on every test
g

gildor

02/09/2021, 2:03 AM
yep, makes sense, but I don’t see why stop timeout is required for tests
k

Kris Wong

02/09/2021, 1:52 PM
the API requires it
stopping the server does a bit of asynchronous work within
runBlocking
I was passing 1 [ms] for both values (grace period and timeout), bumped to 10 and 100
simple fix, but it took me nearly all day to get to the root of the issue